mirror of https://git.sr.ht/~sircmpwn/core-go
78 lines
1.7 KiB
Go
78 lines
1.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"log"
|
|
"time"
|
|
|
|
"git.sr.ht/~sircmpwn/go-bare"
|
|
|
|
"git.sr.ht/~sircmpwn/core-go/crypto"
|
|
)
|
|
|
|
const TokenVersion uint = 0
|
|
|
|
type Timestamp int64
|
|
|
|
func (t Timestamp) Time() time.Time {
|
|
return time.Unix(int64(t), 0).UTC()
|
|
}
|
|
|
|
func ToTimestamp(t time.Time) Timestamp {
|
|
return Timestamp(t.UTC().Unix())
|
|
}
|
|
|
|
type BearerToken struct {
|
|
Version uint
|
|
Expires Timestamp
|
|
Grants string
|
|
ClientID string
|
|
Username string
|
|
}
|
|
|
|
func (bt *BearerToken) Encode() string {
|
|
plain, err := bare.Marshal(bt)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
mac := crypto.BearerHMAC(plain)
|
|
return base64.RawStdEncoding.EncodeToString(append(plain, mac...))
|
|
}
|
|
|
|
func DecodeBearerToken(token string) *BearerToken {
|
|
payload, err := base64.RawStdEncoding.DecodeString(token)
|
|
if err != nil {
|
|
log.Printf("Invalid bearer token: invalid base64 %e", err)
|
|
return nil
|
|
}
|
|
if len(payload) <= 32 {
|
|
log.Printf("Invalid bearer token: payload <32 bytes")
|
|
return nil
|
|
}
|
|
|
|
mac := payload[len(payload)-32:]
|
|
payload = payload[:len(payload)-32]
|
|
if crypto.BearerVerify(payload, mac) == false {
|
|
log.Printf("Invalid bearer token: HMAC verification failed (MAC: [%d]%s; payload: [%d]%s",
|
|
len(mac), hex.EncodeToString(mac), len(payload), hex.EncodeToString(payload))
|
|
return nil
|
|
}
|
|
|
|
var bt BearerToken
|
|
err = bare.Unmarshal(payload, &bt)
|
|
if err != nil {
|
|
log.Printf("Invalid bearer token: BARE unmarshal failed: %e", err)
|
|
return nil
|
|
}
|
|
if bt.Version != TokenVersion {
|
|
log.Printf("Invalid bearer token: invalid token version")
|
|
return nil
|
|
}
|
|
if time.Now().UTC().After(bt.Expires.Time()) {
|
|
log.Printf("Invalid bearer token: token expired")
|
|
return nil
|
|
}
|
|
return &bt
|
|
}
|