From 7a109a763de2122c2b0ba4526426268060a21575 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Wed, 2 Sep 2020 17:52:22 +0200 Subject: [PATCH] feat: service account token exchange --- example/client/github/github.go | 4 ++-- example/internal/mock/storage.go | 2 +- pkg/oidc/authorization.go | 11 ++++++++++- pkg/op/default_op.go | 11 ++++++++++- pkg/op/tokenrequest.go | 34 ++++++++++++++++++++++++++++++++ pkg/rp/default_verifier.go | 17 +++++++++++++--- pkg/utils/crypto.go | 2 -- 7 files changed, 71 insertions(+), 10 deletions(-) diff --git a/example/client/github/github.go b/example/client/github/github.go index 4afa2fb..56d4e8d 100644 --- a/example/client/github/github.go +++ b/example/client/github/github.go @@ -3,11 +3,12 @@ package main import ( "context" "fmt" + "os" + "github.com/caos/oidc/pkg/cli" "github.com/caos/oidc/pkg/rp" "github.com/google/go-github/v31/github" githubOAuth "golang.org/x/oauth2/github" - "os" ) var ( @@ -37,7 +38,6 @@ func main() { if err != nil { fmt.Println("OAuth flow failed") } else { - fmt.Println("OAuth flow success") } } diff --git a/example/internal/mock/storage.go b/example/internal/mock/storage.go index faa62f0..febb28c 100644 --- a/example/internal/mock/storage.go +++ b/example/internal/mock/storage.go @@ -170,7 +170,7 @@ func (s *AuthStorage) GetKeySet(_ context.Context) (*jose.JSONWebKeySet, error) pubkey := s.key.Public() return &jose.JSONWebKeySet{ Keys: []jose.JSONWebKey{ - jose.JSONWebKey{Key: pubkey, Use: "sig", Algorithm: "RS256", KeyID: "1"}, + {Key: pubkey, Use: "sig", Algorithm: "RS256", KeyID: "1"}, }, }, nil } diff --git a/pkg/oidc/authorization.go b/pkg/oidc/authorization.go index 02c5603..f69de28 100644 --- a/pkg/oidc/authorization.go +++ b/pkg/oidc/authorization.go @@ -3,6 +3,7 @@ package oidc import ( "errors" "strings" + "time" "golang.org/x/text/language" ) @@ -24,7 +25,8 @@ const ( PromptConsent Prompt = "consent" PromptSelectAccount Prompt = "select_account" - GrantTypeCode GrantType = "authorization_code" + GrantTypeCode GrantType = "authorization_code" + GrantTypeBearer GrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" BearerToken = "Bearer" ) @@ -100,6 +102,13 @@ type AccessTokenResponse struct { IDToken string `json:"id_token,omitempty" schema:"id_token,omitempty"` } +type JWTTokenRequest struct { + Scopes Scopes `schema:"scope"` + Audience []string `schema:"aud"` + IssuedAt time.Time `schema:"iat"` + ExpiresAt time.Time `schema:"exp"` +} + type TokenExchangeRequest struct { subjectToken string `schema:"subject_token"` subjectTokenType string `schema:"subject_token_type"` diff --git a/pkg/op/default_op.go b/pkg/op/default_op.go index a42da6a..d29fd71 100644 --- a/pkg/op/default_op.go +++ b/pkg/op/default_op.go @@ -273,6 +273,11 @@ func (p *DefaultOP) Signer() Signer { func (p *DefaultOP) Crypto() Crypto { return p.crypto } + +func (p *DefaultOP) Verifier() rp.Verifier { + return p.verifier +} + func (p *DefaultOP) HandleReady(w http.ResponseWriter, r *http.Request) { probes := []ProbesFn{ ReadySigner(p.Signer()), @@ -299,9 +304,13 @@ func (p *DefaultOP) HandleExchange(w http.ResponseWriter, r *http.Request) { RequestError(w, r, ErrInvalidRequest("grant_type missing")) return } - if reqType == string(oidc.GrantTypeCode) { + switch reqType { + case string(oidc.GrantTypeCode): CodeExchange(w, r, p) return + case string(oidc.GrantTypeBearer): + JWTExchange(w, r, p) + return } TokenExchange(w, r, p) } diff --git a/pkg/op/tokenrequest.go b/pkg/op/tokenrequest.go index f924531..7416a1a 100644 --- a/pkg/op/tokenrequest.go +++ b/pkg/op/tokenrequest.go @@ -3,11 +3,13 @@ package op import ( "context" "errors" + "fmt" "net/http" "github.com/gorilla/schema" "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/rp" "github.com/caos/oidc/pkg/utils" ) @@ -20,6 +22,11 @@ type Exchanger interface { AuthMethodPostSupported() bool } +type VerifyExchanger interface { + Exchanger + Verifier() rp.Verifier +} + func CodeExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) { tokenReq, err := ParseAccessTokenRequest(r, exchanger.Decoder()) if err != nil { @@ -116,6 +123,33 @@ func AuthorizeCodeChallenge(ctx context.Context, tokenReq *oidc.AccessTokenReque return authReq, nil } +func JWTExchange(w http.ResponseWriter, r *http.Request, exchanger VerifyExchanger) { + assertion, err := ParseJWTTokenRequest(r, exchanger.Decoder()) + if err != nil { + RequestError(w, r, err) + } + claims, err := exchanger.Verifier().Verify(r.Context(), "", assertion) + fmt.Println(claims, err) + + _ = assertion +} + +func ParseJWTTokenRequest(r *http.Request, decoder *schema.Decoder) (string, error) { + err := r.ParseForm() + if err != nil { + return "", ErrInvalidRequest("error parsing form") + } + tokenReq := new(struct { + Token string `schema:"assertion"` + }) + err = decoder.Decode(tokenReq, r.Form) + if err != nil { + return "", ErrInvalidRequest("error decoding form") + } + //TODO: validations + return tokenReq.Token, nil +} + func TokenExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) { tokenRequest, err := ParseTokenExchangeRequest(w, r) if err != nil { diff --git a/pkg/rp/default_verifier.go b/pkg/rp/default_verifier.go index db599e3..ec2d3e1 100644 --- a/pkg/rp/default_verifier.go +++ b/pkg/rp/default_verifier.go @@ -148,14 +148,25 @@ func DefaultACRVerifier(possibleValues []string) ACRVerifier { //and https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowTokenValidation func (v *DefaultVerifier) Verify(ctx context.Context, accessToken, idTokenString string) (*oidc.IDTokenClaims, error) { v.config.now = time.Now().UTC() - idToken, err := v.VerifyIDToken(ctx, idTokenString) + // idToken, err := v.VerifyIDToken(ctx, idTokenString) + // if err != nil { + // return nil, err + // } + // if err := v.verifyAccessToken(accessToken, idToken.AccessTokenHash, idToken.Signature); err != nil { //TODO: sig from token + // return nil, err + // } + // return idToken, nil + + // TODO: verifiy + decrypted, err := v.decryptToken(idTokenString) if err != nil { return nil, err } - if err := v.verifyAccessToken(accessToken, idToken.AccessTokenHash, idToken.Signature); err != nil { //TODO: sig from token + claims, _, err := v.parseToken(decrypted) + if err != nil { return nil, err } - return idToken, nil + return claims, nil } func (v *DefaultVerifier) now() time.Time { diff --git a/pkg/utils/crypto.go b/pkg/utils/crypto.go index 05acb75..15de1f9 100644 --- a/pkg/utils/crypto.go +++ b/pkg/utils/crypto.go @@ -19,7 +19,6 @@ func EncryptAES(data string, key string) (string, error) { } func EncryptBytesAES(plainText []byte, key string) ([]byte, error) { - block, err := aes.NewCipher([]byte(key)) if err != nil { return nil, err @@ -50,7 +49,6 @@ func DecryptAES(data string, key string) (string, error) { } func DecryptBytesAES(cipherText []byte, key string) ([]byte, error) { - block, err := aes.NewCipher([]byte(key)) if err != nil { return nil, err