feat: service account token exchange

This commit is contained in:
adlerhurst 2020-09-02 17:52:22 +02:00
parent c828290ef1
commit 7a109a763d
7 changed files with 71 additions and 10 deletions

View file

@ -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")
}
}

View file

@ -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
}

View file

@ -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"`

View file

@ -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)
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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