harmonize jwtProfile and existing interfaces / functions
This commit is contained in:
parent
87b30dcd66
commit
a56a4a018a
14 changed files with 259 additions and 632 deletions
|
@ -1,236 +0,0 @@
|
|||
package rp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
grants_tx "github.com/caos/oidc/pkg/oidc/grants/tokenexchange"
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
idTokenKey = "id_token"
|
||||
stateParam = "state"
|
||||
pkceCode = "pkce"
|
||||
)
|
||||
|
||||
//deprecated: use NewRelayingParty instead
|
||||
//DefaultRP implements the `DelegationTokenExchangeRP` interface extending the `RelayingParty` interface
|
||||
type DefaultRP struct {
|
||||
endpoints Endpoints
|
||||
|
||||
oauthConfig oauth2.Config
|
||||
config *Config
|
||||
pkce bool
|
||||
|
||||
httpClient *http.Client
|
||||
cookieHandler *utils.CookieHandler
|
||||
|
||||
errorHandler func(http.ResponseWriter, *http.Request, string, string, string)
|
||||
|
||||
idTokenVerifier IDTokenVerifier
|
||||
verifierOpts []ConfFunc
|
||||
onlyOAuth2 bool
|
||||
}
|
||||
|
||||
func (p *DefaultRP) ErrorHandler() func(http.ResponseWriter, *http.Request, string, string, string) {
|
||||
return p.errorHandler
|
||||
}
|
||||
|
||||
func (p *DefaultRP) OAuthConfig() *oauth2.Config {
|
||||
return &p.oauthConfig
|
||||
}
|
||||
|
||||
func (p *DefaultRP) IsPKCE() bool {
|
||||
return p.pkce
|
||||
}
|
||||
|
||||
func (p *DefaultRP) CookieHandler() *utils.CookieHandler {
|
||||
return p.cookieHandler
|
||||
}
|
||||
|
||||
func (p *DefaultRP) HttpClient() *http.Client {
|
||||
return p.httpClient
|
||||
}
|
||||
|
||||
func (p *DefaultRP) IsOAuth2Only() bool {
|
||||
return p.onlyOAuth2
|
||||
}
|
||||
|
||||
func (p *DefaultRP) IDTokenVerifier() IDTokenVerifier {
|
||||
return p.idTokenVerifier
|
||||
}
|
||||
|
||||
//deprecated: use NewRelayingParty instead
|
||||
//
|
||||
//NewDefaultRP creates `DefaultRP` with the given
|
||||
//Config and possible configOptions
|
||||
//it will run discovery on the provided issuer
|
||||
//if no verifier is provided using the options the `DefaultVerifier` is set
|
||||
func NewDefaultRP(rpConfig *Config, rpOpts ...DefaultRPOpts) (DelegationTokenExchangeRP, error) {
|
||||
foundOpenID := false
|
||||
for _, scope := range rpConfig.Scopes {
|
||||
if scope == "openid" {
|
||||
foundOpenID = true
|
||||
}
|
||||
}
|
||||
|
||||
p := &DefaultRP{
|
||||
config: rpConfig,
|
||||
httpClient: utils.DefaultHTTPClient,
|
||||
onlyOAuth2: !foundOpenID,
|
||||
}
|
||||
|
||||
for _, optFunc := range rpOpts {
|
||||
optFunc(p)
|
||||
}
|
||||
|
||||
if rpConfig.Endpoints.TokenURL != "" && rpConfig.Endpoints.AuthURL != "" {
|
||||
p.oauthConfig = p.getOAuthConfig(rpConfig.Endpoints)
|
||||
} else {
|
||||
if err := p.discover(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if p.errorHandler == nil {
|
||||
p.errorHandler = DefaultErrorHandler
|
||||
}
|
||||
|
||||
if p.idTokenVerifier == nil {
|
||||
p.idTokenVerifier = NewIDTokenVerifier(rpConfig.Issuer, rpConfig.ClientID, NewRemoteKeySet(p.httpClient, p.endpoints.JKWsURL))
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
//DefaultRPOpts is the type for providing dynamic options to the DefaultRP
|
||||
type DefaultRPOpts func(p *DefaultRP)
|
||||
|
||||
/*
|
||||
//WithCookieHandler set a `CookieHandler` for securing the various redirects
|
||||
func WithCookieHandler(cookieHandler *utils.CookieHandler) DefaultRPOpts {
|
||||
return func(p *DefaultRP) {
|
||||
p.cookieHandler = cookieHandler
|
||||
}
|
||||
}
|
||||
|
||||
//WithPKCE sets the RP to use PKCE (oauth2 code challenge)
|
||||
//it also sets a `CookieHandler` for securing the various redirects
|
||||
//and exchanging the code challenge
|
||||
func WithPKCE(cookieHandler *utils.CookieHandler) DefaultRPOpts {
|
||||
return func(p *DefaultRP) {
|
||||
p.pkce = true
|
||||
p.cookieHandler = cookieHandler
|
||||
}
|
||||
}
|
||||
|
||||
//WithHTTPClient provides the ability to set an http client to be used for the relaying party and verifier
|
||||
func WithHTTPClient(client *http.Client) DefaultRPOpts {
|
||||
return func(p *DefaultRP) {
|
||||
p.httpClient = client
|
||||
}
|
||||
}
|
||||
|
||||
func WithVerifierOpts(opts ...ConfFunc) DefaultRPOpts {
|
||||
return func(p *DefaultRP) {
|
||||
p.verifierOpts = opts
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//AuthURL is the `RelayingParty` interface implementation
|
||||
//wrapping the oauth2 `AuthCodeURL`
|
||||
//returning the url of the auth request
|
||||
func (p *DefaultRP) AuthURL(state string, opts ...AuthURLOpt) string {
|
||||
return AuthURL(state, p, opts...)
|
||||
}
|
||||
|
||||
//AuthURL is the `RelayingParty` interface implementation
|
||||
//extending the `AuthURL` method with a http redirect handler
|
||||
func (p *DefaultRP) AuthURLHandler(state string) http.HandlerFunc {
|
||||
return AuthURLHandler(
|
||||
func() string {
|
||||
return state
|
||||
}, p,
|
||||
)
|
||||
}
|
||||
|
||||
//deprecated: Use CodeExchange func and provide a RelayingParty
|
||||
//
|
||||
//AuthURL is the `RelayingParty` interface implementation
|
||||
//handling the oauth2 code exchange, extracting and validating the id_token
|
||||
//returning it parsed together with the oauth2 tokens (access, refresh)
|
||||
func (p *DefaultRP) CodeExchange(ctx context.Context, code string, opts ...CodeExchangeOpt) (tokens *oidc.Tokens, err error) {
|
||||
return CodeExchange(ctx, code, p, opts...)
|
||||
}
|
||||
|
||||
//AuthURL is the `RelayingParty` interface implementation
|
||||
//extending the `CodeExchange` method with callback function
|
||||
func (p *DefaultRP) CodeExchangeHandler(callback func(http.ResponseWriter, *http.Request, *oidc.Tokens, string)) http.HandlerFunc {
|
||||
return CodeExchangeHandler(callback, p)
|
||||
}
|
||||
|
||||
// func (p *DefaultRP) Introspect(ctx context.Context, accessToken string) (oidc.TokenIntrospectResponse, error) {
|
||||
// // req := &http.Request{}
|
||||
// // resp, err := p.httpClient.Do(req)
|
||||
// // if err != nil {
|
||||
|
||||
// // }
|
||||
// // p.endpoints.IntrospectURL
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
func (p *DefaultRP) Userinfo() {}
|
||||
|
||||
//ClientCredentials is the `RelayingParty` interface implementation
|
||||
//handling the oauth2 client credentials grant
|
||||
func (p *DefaultRP) ClientCredentials(ctx context.Context, scopes ...string) (newToken *oauth2.Token, err error) {
|
||||
return ClientCredentials(ctx, p, scopes...)
|
||||
}
|
||||
|
||||
//TokenExchange is the `TokenExchangeRP` interface implementation
|
||||
//handling the oauth2 token exchange (draft)
|
||||
func (p *DefaultRP) TokenExchange(ctx context.Context, request *grants_tx.TokenExchangeRequest) (newToken *oauth2.Token, err error) {
|
||||
return TokenExchange(ctx, request, p)
|
||||
}
|
||||
|
||||
//DelegationTokenExchange is the `TokenExchangeRP` interface implementation
|
||||
//handling the oauth2 token exchange for a delegation token (draft)
|
||||
func (p *DefaultRP) DelegationTokenExchange(ctx context.Context, subjectToken string, reqOpts ...grants_tx.TokenExchangeOption) (newToken *oauth2.Token, err error) {
|
||||
return TokenExchange(ctx, DelegationTokenRequest(subjectToken, reqOpts...), p)
|
||||
}
|
||||
|
||||
func (p *DefaultRP) discover() error {
|
||||
wellKnown := strings.TrimSuffix(p.config.Issuer, "/") + oidc.DiscoveryEndpoint
|
||||
req, err := http.NewRequest("GET", wellKnown, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
discoveryConfig := new(oidc.DiscoveryConfiguration)
|
||||
err = utils.HttpRequest(p.httpClient, req, &discoveryConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.endpoints = GetEndpoints(discoveryConfig)
|
||||
p.oauthConfig = p.getOAuthConfig(p.endpoints.Endpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *DefaultRP) getOAuthConfig(endpoint oauth2.Endpoint) oauth2.Config {
|
||||
return oauth2.Config{
|
||||
ClientID: p.config.ClientID,
|
||||
ClientSecret: p.config.ClientSecret,
|
||||
Endpoint: endpoint,
|
||||
RedirectURL: p.config.CallbackURL,
|
||||
Scopes: p.config.Scopes,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *DefaultRP) Client(ctx context.Context, token *oauth2.Token) *http.Client {
|
||||
return p.oauthConfig.Client(ctx, token)
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
package rp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
)
|
||||
|
||||
//deprecated: use IDTokenVerifier or oidc.Verifier interfaces
|
||||
//DefaultVerifier implements the `Verifier` interface
|
||||
type DefaultVerifier struct {
|
||||
config *verifierConfig
|
||||
keySet oidc.KeySet
|
||||
}
|
||||
|
||||
//ConfFunc is the type for providing dynamic options to the DefaultVerifier
|
||||
type ConfFunc func(*verifierConfig)
|
||||
|
||||
//deprecated: use NewIDTokenVerifier
|
||||
//NewDefaultVerifier creates `DefaultVerifier` with the given
|
||||
//issuer, clientID, keyset and possible configOptions
|
||||
func NewDefaultVerifier(issuer, clientID string, keySet oidc.KeySet, confOpts ...ConfFunc) Verifier {
|
||||
conf := &verifierConfig{
|
||||
issuer: issuer,
|
||||
clientID: clientID,
|
||||
iat: &iatConfig{
|
||||
// offset: time.Duration(500 * time.Millisecond),
|
||||
},
|
||||
}
|
||||
|
||||
for _, opt := range confOpts {
|
||||
if opt != nil {
|
||||
opt(conf)
|
||||
}
|
||||
}
|
||||
return &DefaultVerifier{config: conf, keySet: keySet}
|
||||
}
|
||||
|
||||
//WithIgnoreAudience will turn off validation for audience claim (should only be used for id_token_hints)
|
||||
func WithIgnoreAudience() func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.ignoreAudience = true
|
||||
}
|
||||
}
|
||||
|
||||
//WithIgnoreExpiration will turn off validation for expiration claim (should only be used for id_token_hints)
|
||||
func WithIgnoreExpiration() func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.ignoreExpiration = true
|
||||
}
|
||||
}
|
||||
|
||||
//WithIgnoreIssuedAt will turn off iat claim verification
|
||||
func WithIgnoreIssuedAt() func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.iat.ignore = true
|
||||
}
|
||||
}
|
||||
|
||||
//WithIssuedAtOffset mitigates the risk of iat to be in the future
|
||||
//because of clock skews with the ability to add an offset to the current time
|
||||
func WithIssuedAtOffset(offset time.Duration) func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.iat.offset = offset
|
||||
}
|
||||
}
|
||||
|
||||
//WithIssuedAtMaxAge provides the ability to define the maximum duration between iat and now
|
||||
func WithIssuedAtMaxAge(maxAge time.Duration) func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.iat.maxAge = maxAge
|
||||
}
|
||||
}
|
||||
|
||||
//WithNonce TODO: ?
|
||||
func WithNonce(nonce string) func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.nonce = nonce
|
||||
}
|
||||
}
|
||||
|
||||
//WithACRVerifier sets the verifier for the acr claim
|
||||
func WithACRVerifier(verifier oidc.ACRVerifier) func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.acr = verifier
|
||||
}
|
||||
}
|
||||
|
||||
//WithAuthTimeMaxAge provides the ability to define the maximum duration between auth_time and now
|
||||
func WithAuthTimeMaxAge(maxAge time.Duration) func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.maxAge = maxAge
|
||||
}
|
||||
}
|
||||
|
||||
//WithSupportedSigningAlgorithms overwrites the default RS256 signing algorithm
|
||||
func WithSupportedSigningAlgorithms(algs ...string) func(*verifierConfig) {
|
||||
return func(conf *verifierConfig) {
|
||||
conf.supportedSignAlgs = algs
|
||||
}
|
||||
}
|
||||
|
||||
type verifierConfig struct {
|
||||
issuer string
|
||||
clientID string
|
||||
nonce string
|
||||
ignoreAudience bool
|
||||
ignoreExpiration bool
|
||||
iat *iatConfig
|
||||
acr oidc.ACRVerifier
|
||||
maxAge time.Duration
|
||||
supportedSignAlgs []string
|
||||
|
||||
// httpClient *http.Client
|
||||
|
||||
now time.Time
|
||||
}
|
||||
|
||||
type iatConfig struct {
|
||||
ignore bool
|
||||
offset time.Duration
|
||||
maxAge time.Duration
|
||||
}
|
||||
|
||||
//deprecated: use oidc.DefaultACRVerifier directly
|
||||
//DefaultACRVerifier implements `ACRVerifier` returning an error
|
||||
//if non of the provided values matches the acr claim
|
||||
func DefaultACRVerifier(possibleValues []string) oidc.ACRVerifier {
|
||||
return oidc.DefaultACRVerifier(possibleValues)
|
||||
}
|
||||
|
||||
//deprecated: use VerifyTokens(ctx context.Context, accessToken, idTokenString string, v IDTokenVerifier) (*oidc.IDTokenClaims, error) instead
|
||||
//Verify implements the `Verify` method of the `Verifier` interface
|
||||
//according to https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||
//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()
|
||||
return VerifyTokens(ctx, accessToken, idTokenString, v)
|
||||
}
|
||||
|
||||
//deprecated: use VerifyIDToken(ctx context.Context, token string, v IDTokenVerifier) (*oidc.IDTokenClaims, error) instead
|
||||
//Verify implements the `VerifyIDToken` method of the `Verifier` interface
|
||||
//according to https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||
func (v *DefaultVerifier) VerifyIDToken(ctx context.Context, idTokenString string) (*oidc.IDTokenClaims, error) {
|
||||
return VerifyIDToken(ctx, idTokenString, v)
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) now() time.Time {
|
||||
if v.config.now.IsZero() {
|
||||
v.config.now = time.Now().UTC().Round(time.Second)
|
||||
}
|
||||
return v.config.now
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) Issuer() string {
|
||||
return v.config.issuer
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) ClientID() string {
|
||||
return v.config.clientID
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) SupportedSignAlgs() []string {
|
||||
return v.config.supportedSignAlgs
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) KeySet() oidc.KeySet {
|
||||
return v.keySet
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) ACR() oidc.ACRVerifier {
|
||||
return v.config.acr
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) MaxAge() time.Duration {
|
||||
return v.config.maxAge
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) MaxAgeIAT() time.Duration {
|
||||
return v.config.iat.maxAge
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) Offset() time.Duration {
|
||||
return v.config.iat.offset
|
||||
}
|
||||
|
||||
func (v *DefaultVerifier) Nonce(ctx context.Context) string {
|
||||
return ""
|
||||
}
|
|
@ -74,7 +74,7 @@ func (r *remoteKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSig
|
|||
}
|
||||
|
||||
keys := r.keysFromCache()
|
||||
payload, err, ok := CheckKey(keyID, keys, jws)
|
||||
payload, err, ok := oidc.CheckKey(keyID, jws, keys...)
|
||||
if ok {
|
||||
return payload, err
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ func (r *remoteKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSig
|
|||
return nil, fmt.Errorf("fetching keys %v", err)
|
||||
}
|
||||
|
||||
payload, err, ok = CheckKey(keyID, keys, jws)
|
||||
payload, err, ok = oidc.CheckKey(keyID, jws, keys...)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid kid")
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package rp
|
||||
|
||||
import (
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
func CheckKey(keyID string, keys []jose.JSONWebKey, jws *jose.JSONWebSignature) ([]byte, error, bool) {
|
||||
for _, key := range keys {
|
||||
if keyID == "" || key.KeyID == keyID {
|
||||
payload, err := jws.Verify(&key)
|
||||
return payload, err, true
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
|
@ -13,6 +13,12 @@ import (
|
|||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const (
|
||||
idTokenKey = "id_token"
|
||||
stateParam = "state"
|
||||
pkceCode = "pkce"
|
||||
)
|
||||
|
||||
//RelayingParty declares the minimal interface for oidc clients
|
||||
type RelayingParty interface {
|
||||
//OAuthConfig returns the oauth2 Config
|
||||
|
@ -27,54 +33,12 @@ type RelayingParty interface {
|
|||
//Client return a standard http client where the token can be used
|
||||
Client(ctx context.Context, token *oauth2.Token) *http.Client
|
||||
|
||||
/*
|
||||
//AuthURL returns the authorization endpoint with a given state
|
||||
AuthURL(state string, opts ...AuthURLOpt) string
|
||||
|
||||
//AuthURLHandler should implement the AuthURL func as http.HandlerFunc
|
||||
//(redirecting to the auth endpoint)
|
||||
AuthURLHandler(state string) http.HandlerFunc
|
||||
|
||||
//CodeExchange implements the OIDC Token Request (oauth2 Authorization Code Grant)
|
||||
//returning an `Access Token` and `ID Token Claims`
|
||||
CodeExchange(ctx context.Context, code string, opts ...CodeExchangeOpt) (*oidc.Tokens, error)
|
||||
|
||||
//CodeExchangeHandler extends the CodeExchange func,
|
||||
//calling the provided callback func on success with additional returned `state`
|
||||
CodeExchangeHandler(callback func(http.ResponseWriter, *http.Request, *oidc.Tokens, string)) http.HandlerFunc
|
||||
|
||||
//ClientCredentials implements the oauth2 Client Credentials Grant
|
||||
//requesting an `Access Token` for the client itself, without user context
|
||||
ClientCredentials(ctx context.Context, scopes ...string) (*oauth2.Token, error)
|
||||
|
||||
//Introspects calls the Introspect Endpoint
|
||||
//for validating an (access) token
|
||||
// Introspect(ctx context.Context, token string) (TokenIntrospectResponse, error)
|
||||
|
||||
//Userinfo implements the OIDC Userinfo call
|
||||
//returning the info of the user for the requested scopes of an access token
|
||||
Userinfo()
|
||||
*/
|
||||
HttpClient() *http.Client
|
||||
IsOAuth2Only() bool
|
||||
IDTokenVerifier() IDTokenVerifier
|
||||
ErrorHandler() func(http.ResponseWriter, *http.Request, string, string, string)
|
||||
}
|
||||
|
||||
//
|
||||
////PasswortGrantRP extends the `RelayingParty` interface with the oauth2 `Password Grant`
|
||||
////
|
||||
////This interface is separated from the standard `RelayingParty` interface as the `password grant`
|
||||
////is part of the oauth2 and therefore OIDC specification, but should only be used when there's no
|
||||
////other possibility, so IMHO never ever. Ever.
|
||||
//type PasswortGrantRP interface {
|
||||
// RelayingParty
|
||||
//
|
||||
// //PasswordGrant implements the oauth2 `Password Grant`,
|
||||
// //requesting an access token with the users `username` and `password`
|
||||
// PasswordGrant(context.Context, string, string) (*oauth2.Token, error)
|
||||
//}
|
||||
|
||||
var (
|
||||
DefaultErrorHandler = func(w http.ResponseWriter, r *http.Request, errorType string, errorDesc string, state string) {
|
||||
http.Error(w, errorType+": "+errorDesc, http.StatusInternalServerError)
|
||||
|
@ -84,9 +48,8 @@ var (
|
|||
type relayingParty struct {
|
||||
endpoints Endpoints
|
||||
|
||||
oauthConfig *oauth2.Config
|
||||
config *Configuration
|
||||
pkce bool
|
||||
config *Configuration
|
||||
pkce bool
|
||||
|
||||
httpClient *http.Client
|
||||
cookieHandler *utils.CookieHandler
|
||||
|
@ -94,12 +57,12 @@ type relayingParty struct {
|
|||
errorHandler func(http.ResponseWriter, *http.Request, string, string, string)
|
||||
|
||||
idTokenVerifier IDTokenVerifier
|
||||
verifierOpts []ConfFunc
|
||||
verifierOpts []VerifierOption
|
||||
oauth2Only bool
|
||||
}
|
||||
|
||||
func (rp *relayingParty) OAuthConfig() *oauth2.Config {
|
||||
return rp.oauthConfig
|
||||
return rp.config.Config
|
||||
}
|
||||
|
||||
func (rp *relayingParty) IsPKCE() bool {
|
||||
|
@ -119,11 +82,14 @@ func (rp *relayingParty) IsOAuth2Only() bool {
|
|||
}
|
||||
|
||||
func (rp *relayingParty) IDTokenVerifier() IDTokenVerifier {
|
||||
if rp.idTokenVerifier == nil {
|
||||
rp.idTokenVerifier = NewIDTokenVerifier(rp.config.Issuer, rp.config.ClientID, NewRemoteKeySet(rp.httpClient, rp.endpoints.JKWsURL), rp.verifierOpts...)
|
||||
}
|
||||
return rp.idTokenVerifier
|
||||
}
|
||||
|
||||
func (rp *relayingParty) Client(ctx context.Context, token *oauth2.Token) *http.Client {
|
||||
panic("implement me")
|
||||
return rp.config.Config.Client(ctx, token)
|
||||
}
|
||||
|
||||
func (rp *relayingParty) ErrorHandler() func(http.ResponseWriter, *http.Request, string, string, string) {
|
||||
|
@ -147,15 +113,12 @@ func NewRelayingParty(config *Configuration, options ...Option) (RelayingParty,
|
|||
optFunc(rp)
|
||||
}
|
||||
|
||||
rp.oauthConfig = config.Config
|
||||
if config.Endpoint.AuthURL != "" && config.Endpoint.TokenURL != "" {
|
||||
rp.oauthConfig = config.Config
|
||||
} else {
|
||||
if isOpenID && config.Endpoint.AuthURL == "" && config.Endpoint.TokenURL == "" {
|
||||
endpoints, err := Discover(config.Issuer, rp.httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rp.oauthConfig.Endpoint = endpoints.Endpoint
|
||||
rp.config.Endpoint = endpoints.Endpoint
|
||||
rp.endpoints = endpoints
|
||||
}
|
||||
|
||||
|
@ -170,6 +133,47 @@ func NewRelayingParty(config *Configuration, options ...Option) (RelayingParty,
|
|||
return rp, nil
|
||||
}
|
||||
|
||||
func NewRelayingParty2(clientID, clientSecret, redirectURI string, options ...Option) (RelayingParty, error) {
|
||||
rp := &relayingParty{
|
||||
config: &Configuration{
|
||||
Config: &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectURI,
|
||||
},
|
||||
},
|
||||
httpClient: utils.DefaultHTTPClient,
|
||||
oauth2Only: true,
|
||||
}
|
||||
|
||||
for _, optFunc := range options {
|
||||
optFunc(rp)
|
||||
}
|
||||
|
||||
if !rp.oauth2Only && rp.config.Endpoint.AuthURL == "" && rp.config.Endpoint.TokenURL == "" {
|
||||
endpoints, err := Discover(rp.config.Issuer, rp.httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rp.config.Endpoint = endpoints.Endpoint
|
||||
rp.endpoints = endpoints
|
||||
}
|
||||
|
||||
if rp.errorHandler == nil {
|
||||
rp.errorHandler = DefaultErrorHandler
|
||||
}
|
||||
|
||||
return rp, nil
|
||||
}
|
||||
|
||||
func WithOIDC(issuer string, scopes []string) Option {
|
||||
return func(rp *relayingParty) {
|
||||
rp.config.Issuer = issuer
|
||||
rp.config.Scopes = scopes
|
||||
rp.oauth2Only = false
|
||||
}
|
||||
}
|
||||
|
||||
//DefaultRPOpts is the type for providing dynamic options to the DefaultRP
|
||||
type Option func(*relayingParty)
|
||||
|
||||
|
@ -197,7 +201,7 @@ func WithHTTPClient(client *http.Client) Option {
|
|||
}
|
||||
}
|
||||
|
||||
func WithVerifierOpts(opts ...ConfFunc) Option {
|
||||
func WithVerifierOpts(opts ...VerifierOption) Option {
|
||||
return func(rp *relayingParty) {
|
||||
rp.verifierOpts = opts
|
||||
}
|
||||
|
@ -419,13 +423,3 @@ func isOpenID(scopes []string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//deprecated: use Configuration instead
|
||||
type Config struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
CallbackURL string
|
||||
Issuer string
|
||||
Scopes []string
|
||||
Endpoints oauth2.Endpoint
|
||||
}
|
||||
|
|
|
@ -103,16 +103,68 @@ func VerifyAccessToken(accessToken, atHash string, sigAlgorithm jose.SignatureAl
|
|||
|
||||
//NewIDTokenVerifier returns an implementation of `IDTokenVerifier`
|
||||
//for `VerifyTokens` and `VerifyIDToken`
|
||||
func NewIDTokenVerifier(issuer, clientID string, keySet oidc.KeySet) IDTokenVerifier {
|
||||
return &idTokenVerifier{
|
||||
func NewIDTokenVerifier(issuer, clientID string, keySet oidc.KeySet, options ...VerifierOption) IDTokenVerifier {
|
||||
v := &idTokenVerifier{
|
||||
issuer: issuer,
|
||||
clientID: clientID,
|
||||
keySet: keySet,
|
||||
offset: 5 * time.Second,
|
||||
offset: 1 * time.Second,
|
||||
nonce: func(_ context.Context) string {
|
||||
return ""
|
||||
},
|
||||
}
|
||||
|
||||
for _, opts := range options {
|
||||
opts(v)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
//VerifierOption is the type for providing dynamic options to the IDTokenVerifier
|
||||
type VerifierOption func(*idTokenVerifier)
|
||||
|
||||
//WithIssuedAtOffset mitigates the risk of iat to be in the future
|
||||
//because of clock skews with the ability to add an offset to the current time
|
||||
func WithIssuedAtOffset(offset time.Duration) func(*idTokenVerifier) {
|
||||
return func(v *idTokenVerifier) {
|
||||
v.offset = offset
|
||||
}
|
||||
}
|
||||
|
||||
//WithIssuedAtMaxAge provides the ability to define the maximum duration between iat and now
|
||||
func WithIssuedAtMaxAge(maxAge time.Duration) func(*idTokenVerifier) {
|
||||
return func(v *idTokenVerifier) {
|
||||
v.maxAge = maxAge
|
||||
}
|
||||
}
|
||||
|
||||
//WithNonce sets the function to check the nonce
|
||||
func WithNonce(nonce func(context.Context) string) VerifierOption {
|
||||
return func(v *idTokenVerifier) {
|
||||
v.nonce = nonce
|
||||
}
|
||||
}
|
||||
|
||||
//WithACRVerifier sets the verifier for the acr claim
|
||||
func WithACRVerifier(verifier oidc.ACRVerifier) VerifierOption {
|
||||
return func(v *idTokenVerifier) {
|
||||
v.acr = verifier
|
||||
}
|
||||
}
|
||||
|
||||
//WithAuthTimeMaxAge provides the ability to define the maximum duration between auth_time and now
|
||||
func WithAuthTimeMaxAge(maxAge time.Duration) VerifierOption {
|
||||
return func(v *idTokenVerifier) {
|
||||
v.maxAge = maxAge
|
||||
}
|
||||
}
|
||||
|
||||
//WithSupportedSigningAlgorithms overwrites the default RS256 signing algorithm
|
||||
func WithSupportedSigningAlgorithms(algs ...string) VerifierOption {
|
||||
return func(v *idTokenVerifier) {
|
||||
v.supportedSignAlgs = algs
|
||||
}
|
||||
}
|
||||
|
||||
type idTokenVerifier struct {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue