mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
133 lines
3.3 KiB
Go
133 lines
3.3 KiB
Go
package didkey
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
"github.com/golang-jwt/jwt"
|
||
|
|
"github.com/ucan-wg/go-ucan"
|
||
|
|
"github.com/ucan-wg/go-ucan/didkey"
|
||
|
|
)
|
||
|
|
|
||
|
|
// DIDPubKeyResolver turns did:key Decentralized IDentifiers into a public key,
|
||
|
|
// possibly using a network request
|
||
|
|
type DIDPubKeyResolver interface {
|
||
|
|
ResolveDIDKey(ctx context.Context, did string) (ID, error)
|
||
|
|
}
|
||
|
|
|
||
|
|
// TokenParser parses a raw string into a Token
|
||
|
|
type TokenParser struct {
|
||
|
|
ap ucan.AttenuationConstructorFunc
|
||
|
|
cidr ucan.CIDBytesResolver
|
||
|
|
didr DIDPubKeyResolver
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewTokenParser constructs a token parser
|
||
|
|
func NewTokenParser(ap ucan.AttenuationConstructorFunc, didr DIDPubKeyResolver, cidr ucan.CIDBytesResolver) *TokenParser {
|
||
|
|
return &TokenParser{
|
||
|
|
ap: ap,
|
||
|
|
cidr: cidr,
|
||
|
|
didr: didr,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ParseAndVerify will parse, validate and return a token
|
||
|
|
func (p *TokenParser) ParseAndVerify(ctx context.Context, raw string) (*ucan.Token, error) {
|
||
|
|
return p.parseAndVerify(ctx, raw, nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p *TokenParser) parseAndVerify(ctx context.Context, raw string, _ *ucan.Token) (*ucan.Token, error) {
|
||
|
|
tok, err := jwt.Parse(raw, p.matchVerifyKeyFunc(ctx))
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("parsing UCAN: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
mc, ok := tok.Claims.(jwt.MapClaims)
|
||
|
|
if !ok {
|
||
|
|
return nil, fmt.Errorf("parser fail")
|
||
|
|
}
|
||
|
|
|
||
|
|
var iss didkey.ID
|
||
|
|
// TODO(b5): we're double parsing here b/c the jwt lib we're using doesn't expose
|
||
|
|
// an API (that I know of) for storing parsed issuer / audience
|
||
|
|
if issStr, ok := mc["iss"].(string); ok {
|
||
|
|
iss, err = didkey.Parse(issStr)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return nil, fmt.Errorf(`"iss" key is not in claims`)
|
||
|
|
}
|
||
|
|
|
||
|
|
var aud didkey.ID
|
||
|
|
// TODO(b5): we're double parsing here b/c the jwt lib we're using doesn't expose
|
||
|
|
// an API (that I know of) for storing parsed issuer / audience
|
||
|
|
if audStr, ok := mc["aud"].(string); ok {
|
||
|
|
aud, err = didkey.Parse(audStr)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return nil, fmt.Errorf(`"aud" key is not in claims`)
|
||
|
|
}
|
||
|
|
|
||
|
|
var att ucan.Attenuations
|
||
|
|
if acci, ok := mc[ucan.AttKey].([]interface{}); ok {
|
||
|
|
for i, a := range acci {
|
||
|
|
if mapv, ok := a.(map[string]interface{}); ok {
|
||
|
|
a, err := p.ap(mapv)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
att = append(att, a)
|
||
|
|
} else {
|
||
|
|
return nil, fmt.Errorf(`"att[%d]" is not an object`, i)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return nil, fmt.Errorf(`"att" key is not an array`)
|
||
|
|
}
|
||
|
|
|
||
|
|
var prf []ucan.Proof
|
||
|
|
if prfi, ok := mc[ucan.PrfKey].([]interface{}); ok {
|
||
|
|
for i, a := range prfi {
|
||
|
|
if pStr, ok := a.(string); ok {
|
||
|
|
prf = append(prf, ucan.Proof(pStr))
|
||
|
|
} else {
|
||
|
|
return nil, fmt.Errorf(`"prf[%d]" is not a string`, i)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if mc[ucan.PrfKey] != nil {
|
||
|
|
return nil, fmt.Errorf(`"prf" key is not an array`)
|
||
|
|
}
|
||
|
|
|
||
|
|
return &ucan.Token{
|
||
|
|
Raw: raw,
|
||
|
|
Issuer: iss,
|
||
|
|
Audience: aud,
|
||
|
|
Attenuations: att,
|
||
|
|
Proofs: prf,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p *TokenParser) matchVerifyKeyFunc(ctx context.Context) func(tok *jwt.Token) (interface{}, error) {
|
||
|
|
return func(tok *jwt.Token) (interface{}, error) {
|
||
|
|
mc, ok := tok.Claims.(jwt.MapClaims)
|
||
|
|
if !ok {
|
||
|
|
return nil, fmt.Errorf("parser fail")
|
||
|
|
}
|
||
|
|
|
||
|
|
iss, ok := mc["iss"].(string)
|
||
|
|
if !ok {
|
||
|
|
return nil, fmt.Errorf(`"iss" claims key is required`)
|
||
|
|
}
|
||
|
|
|
||
|
|
id, err := p.didr.ResolveDIDKey(ctx, iss)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return id.VerifyKey()
|
||
|
|
}
|
||
|
|
}
|