core-go/auth/bearer.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
}