feat: service account token exchange
This commit is contained in:
parent
c828290ef1
commit
7a109a763d
7 changed files with 71 additions and 10 deletions
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue