chore(linting): apply gofumpt & goimports to all .go files (#225)

This commit is contained in:
David Sharnoff 2022-10-05 00:33:10 -07:00 committed by GitHub
parent c4b7ef9160
commit b5da6ec29b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 539 additions and 479 deletions

View file

@ -34,14 +34,14 @@ func main() {
router := mux.NewRouter() router := mux.NewRouter()
//public url accessible without any authorization // public url accessible without any authorization
//will print `OK` and current timestamp // will print `OK` and current timestamp
router.HandleFunc(publicURL, func(w http.ResponseWriter, r *http.Request) { router.HandleFunc(publicURL, func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK " + time.Now().String())) w.Write([]byte("OK " + time.Now().String()))
}) })
//protected url which needs an active token // protected url which needs an active token
//will print the result of the introspection endpoint on success // will print the result of the introspection endpoint on success
router.HandleFunc(protectedURL, func(w http.ResponseWriter, r *http.Request) { router.HandleFunc(protectedURL, func(w http.ResponseWriter, r *http.Request) {
ok, token := checkToken(w, r) ok, token := checkToken(w, r)
if !ok { if !ok {
@ -60,9 +60,9 @@ func main() {
w.Write(data) w.Write(data)
}) })
//protected url which needs an active token and checks if the response of the introspect endpoint // protected url which needs an active token and checks if the response of the introspect endpoint
//contains a requested claim with the required (string) value // contains a requested claim with the required (string) value
//e.g. /protected/username/livio@caos.ch // e.g. /protected/username/livio@caos.ch
router.HandleFunc(protectedClaimURL, func(w http.ResponseWriter, r *http.Request) { router.HandleFunc(protectedClaimURL, func(w http.ResponseWriter, r *http.Request) {
ok, token := checkToken(w, r) ok, token := checkToken(w, r)
if !ok { if !ok {

View file

@ -48,18 +48,18 @@ func main() {
logrus.Fatalf("error creating provider %s", err.Error()) logrus.Fatalf("error creating provider %s", err.Error())
} }
//generate some state (representing the state of the user in your application, // generate some state (representing the state of the user in your application,
//e.g. the page where he was before sending him to login // e.g. the page where he was before sending him to login
state := func() string { state := func() string {
return uuid.New().String() return uuid.New().String()
} }
//register the AuthURLHandler at your preferred path // register the AuthURLHandler at your preferred path
//the AuthURLHandler creates the auth request and redirects the user to the auth server // the AuthURLHandler creates the auth request and redirects the user to the auth server
//including state handling with secure cookie and the possibility to use PKCE // including state handling with secure cookie and the possibility to use PKCE
http.Handle("/login", rp.AuthURLHandler(state, provider)) http.Handle("/login", rp.AuthURLHandler(state, provider))
//for demonstration purposes the returned userinfo response is written as JSON object onto response // for demonstration purposes the returned userinfo response is written as JSON object onto response
marshalUserinfo := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp rp.RelyingParty, info oidc.UserInfo) { marshalUserinfo := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp rp.RelyingParty, info oidc.UserInfo) {
data, err := json.Marshal(info) data, err := json.Marshal(info)
if err != nil { if err != nil {
@ -69,9 +69,9 @@ func main() {
w.Write(data) w.Write(data)
} }
//you could also just take the access_token and id_token without calling the userinfo endpoint: // you could also just take the access_token and id_token without calling the userinfo endpoint:
// //
//marshalToken := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp rp.RelyingParty) { // marshalToken := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp rp.RelyingParty) {
// data, err := json.Marshal(tokens) // data, err := json.Marshal(tokens)
// if err != nil { // if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError) // http.Error(w, err.Error(), http.StatusInternalServerError)
@ -80,16 +80,16 @@ func main() {
// w.Write(data) // w.Write(data)
//} //}
//register the CodeExchangeHandler at the callbackPath // register the CodeExchangeHandler at the callbackPath
//the CodeExchangeHandler handles the auth response, creates the token request and calls the callback function // the CodeExchangeHandler handles the auth response, creates the token request and calls the callback function
//with the returned tokens from the token endpoint // with the returned tokens from the token endpoint
//in this example the callback function itself is wrapped by the UserinfoCallback which // in this example the callback function itself is wrapped by the UserinfoCallback which
//will call the Userinfo endpoint, check the sub and pass the info into the callback function // will call the Userinfo endpoint, check the sub and pass the info into the callback function
http.Handle(callbackPath, rp.CodeExchangeHandler(rp.UserinfoCallback(marshalUserinfo), provider)) http.Handle(callbackPath, rp.CodeExchangeHandler(rp.UserinfoCallback(marshalUserinfo), provider))
//if you would use the callback without calling the userinfo endpoint, simply switch the callback handler for: // if you would use the callback without calling the userinfo endpoint, simply switch the callback handler for:
// //
//http.Handle(callbackPath, rp.CodeExchangeHandler(marshalToken, provider)) // http.Handle(callbackPath, rp.CodeExchangeHandler(marshalToken, provider))
lis := fmt.Sprintf("127.0.0.1:%s", port) lis := fmt.Sprintf("127.0.0.1:%s", port)
logrus.Infof("listening on http://%s/", lis) logrus.Infof("listening on http://%s/", lis)

View file

@ -16,9 +16,7 @@ import (
"github.com/zitadel/oidc/pkg/client/profile" "github.com/zitadel/oidc/pkg/client/profile"
) )
var ( var client = http.DefaultClient
client = http.DefaultClient
)
func main() { func main() {
keyPath := os.Getenv("KEY_PATH") keyPath := os.Getenv("KEY_PATH")
@ -145,7 +143,6 @@ func main() {
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }
}) })
lis := fmt.Sprintf("127.0.0.1:%s", port) lis := fmt.Sprintf("127.0.0.1:%s", port)
logrus.Infof("listening on http://%s/", lis) logrus.Infof("listening on http://%s/", lis)

View file

@ -15,20 +15,17 @@ import (
"github.com/zitadel/oidc/pkg/oidc" "github.com/zitadel/oidc/pkg/oidc"
) )
var ( var Encoder = func() httphelper.Encoder {
Encoder = func() httphelper.Encoder { e := schema.NewEncoder()
e := schema.NewEncoder() e.RegisterEncoder(oidc.SpaceDelimitedArray{}, func(value reflect.Value) string {
e.RegisterEncoder(oidc.SpaceDelimitedArray{}, func(value reflect.Value) string { return value.Interface().(oidc.SpaceDelimitedArray).Encode()
return value.Interface().(oidc.SpaceDelimitedArray).Encode() })
}) return e
return e }()
}()
)
//Discover calls the discovery endpoint of the provided issuer and returns its configuration // Discover calls the discovery endpoint of the provided issuer and returns its configuration
//It accepts an optional argument "wellknownUrl" which can be used to overide the dicovery endpoint url // It accepts an optional argument "wellknownUrl" which can be used to overide the dicovery endpoint url
func Discover(issuer string, httpClient *http.Client, wellKnownUrl ...string) (*oidc.DiscoveryConfiguration, error) { func Discover(issuer string, httpClient *http.Client, wellKnownUrl ...string) (*oidc.DiscoveryConfiguration, error) {
wellKnown := strings.TrimSuffix(issuer, "/") + oidc.DiscoveryEndpoint wellKnown := strings.TrimSuffix(issuer, "/") + oidc.DiscoveryEndpoint
if len(wellKnownUrl) == 1 && wellKnownUrl[0] != "" { if len(wellKnownUrl) == 1 && wellKnownUrl[0] != "" {
wellKnown = wellKnownUrl[0] wellKnown = wellKnownUrl[0]

View file

@ -14,12 +14,12 @@ type keyFile struct {
Type string `json:"type"` // serviceaccount or application Type string `json:"type"` // serviceaccount or application
KeyID string `json:"keyId"` KeyID string `json:"keyId"`
Key string `json:"key"` Key string `json:"key"`
Issuer string `json:"issuer"` //not yet in file Issuer string `json:"issuer"` // not yet in file
//serviceaccount // serviceaccount
UserID string `json:"userId"` UserID string `json:"userId"`
//application // application
ClientID string `json:"clientId"` ClientID string `json:"clientId"`
} }

View file

@ -11,9 +11,9 @@ import (
"github.com/zitadel/oidc/pkg/oidc" "github.com/zitadel/oidc/pkg/oidc"
) )
//jwtProfileTokenSource implement the oauth2.TokenSource // jwtProfileTokenSource implement the oauth2.TokenSource
//it will request a token using the OAuth2 JWT Profile Grant // it will request a token using the OAuth2 JWT Profile Grant
//therefore sending an `assertion` by singing a JWT with the provided private key // therefore sending an `assertion` by singing a JWT with the provided private key
type jwtProfileTokenSource struct { type jwtProfileTokenSource struct {
clientID string clientID string
audience []string audience []string

View file

@ -4,8 +4,8 @@ import (
"github.com/zitadel/oidc/pkg/oidc/grants/tokenexchange" "github.com/zitadel/oidc/pkg/oidc/grants/tokenexchange"
) )
//DelegationTokenRequest is an implementation of TokenExchangeRequest // DelegationTokenRequest is an implementation of TokenExchangeRequest
//it exchanges an "urn:ietf:params:oauth:token-type:access_token" with an optional // it exchanges an "urn:ietf:params:oauth:token-type:access_token" with an optional
//"urn:ietf:params:oauth:token-type:access_token" actor token for an //"urn:ietf:params:oauth:token-type:access_token" actor token for an
//"urn:ietf:params:oauth:token-type:access_token" delegation token //"urn:ietf:params:oauth:token-type:access_token" delegation token
func DelegationTokenRequest(subjectToken string, opts ...tokenexchange.TokenExchangeOption) *tokenexchange.TokenExchangeRequest { func DelegationTokenRequest(subjectToken string, opts ...tokenexchange.TokenExchangeOption) *tokenexchange.TokenExchangeRequest {

View file

@ -21,12 +21,12 @@ func NewRemoteKeySet(client *http.Client, jwksURL string, opts ...func(*remoteKe
return keyset return keyset
} }
//SkipRemoteCheck will suppress checking for new remote keys if signature validation fails with cached keys // SkipRemoteCheck will suppress checking for new remote keys if signature validation fails with cached keys
//and no kid header is set in the JWT // and no kid header is set in the JWT
// //
//this might be handy to save some unnecessary round trips in cases where the JWT does not contain a kid header and // this might be handy to save some unnecessary round trips in cases where the JWT does not contain a kid header and
//there is only a single remote key // there is only a single remote key
//please notice that remote keys will then only be fetched if cached keys are empty // please notice that remote keys will then only be fetched if cached keys are empty
func SkipRemoteCheck() func(set *remoteKeySet) { func SkipRemoteCheck() func(set *remoteKeySet) {
return func(set *remoteKeySet) { return func(set *remoteKeySet) {
set.skipRemoteCheck = true set.skipRemoteCheck = true
@ -97,15 +97,15 @@ func (r *remoteKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSig
return r.verifySignatureRemote(ctx, jws, keyID, alg) return r.verifySignatureRemote(ctx, jws, keyID, alg)
} }
//verifySignatureCached checks for a matching key in the cached key list // verifySignatureCached checks for a matching key in the cached key list
// //
//if there is only one possible, it tries to verify the signature and will return the payload if successful // if there is only one possible, it tries to verify the signature and will return the payload if successful
// //
//it only returns an error if signature validation fails and keys exactMatch which is if either: // it only returns an error if signature validation fails and keys exactMatch which is if either:
// - both kid are empty and skipRemoteCheck is set to true // - both kid are empty and skipRemoteCheck is set to true
// - or both (JWT and JWK) kid are equal // - or both (JWT and JWK) kid are equal
// //
//otherwise it will return no error (so remote keys will be loaded) // otherwise it will return no error (so remote keys will be loaded)
func (r *remoteKeySet) verifySignatureCached(jws *jose.JSONWebSignature, keyID, alg string) ([]byte, error) { func (r *remoteKeySet) verifySignatureCached(jws *jose.JSONWebSignature, keyID, alg string) ([]byte, error) {
keys := r.keysFromCache() keys := r.keysFromCache()
if len(keys) == 0 { if len(keys) == 0 {
@ -113,7 +113,7 @@ func (r *remoteKeySet) verifySignatureCached(jws *jose.JSONWebSignature, keyID,
} }
key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keys...) key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keys...)
if err != nil { if err != nil {
//no key / multiple found, try with remote keys // no key / multiple found, try with remote keys
return nil, nil //nolint:nilerr return nil, nil //nolint:nilerr
} }
payload, err := jws.Verify(&key) payload, err := jws.Verify(&key)
@ -121,7 +121,7 @@ func (r *remoteKeySet) verifySignatureCached(jws *jose.JSONWebSignature, keyID,
return payload, nil return payload, nil
} }
if !r.exactMatch(key.KeyID, keyID) { if !r.exactMatch(key.KeyID, keyID) {
//no exact key match, try getting better match with remote keys // no exact key match, try getting better match with remote keys
return nil, nil return nil, nil
} }
return nil, fmt.Errorf("signature verification failed: %w", err) return nil, fmt.Errorf("signature verification failed: %w", err)
@ -213,11 +213,11 @@ func (r *remoteKeySet) fetchRemoteKeys(ctx context.Context) ([]jose.JSONWebKey,
return keySet.Keys, nil return keySet.Keys, nil
} }
//jsonWebKeySet is an alias for jose.JSONWebKeySet which ignores unknown key types (kty) // jsonWebKeySet is an alias for jose.JSONWebKeySet which ignores unknown key types (kty)
type jsonWebKeySet jose.JSONWebKeySet type jsonWebKeySet jose.JSONWebKeySet
//UnmarshalJSON overrides the default jose.JSONWebKeySet method to ignore any error // UnmarshalJSON overrides the default jose.JSONWebKeySet method to ignore any error
//which might occur because of unknown key types (kty) // which might occur because of unknown key types (kty)
func (k *jsonWebKeySet) UnmarshalJSON(data []byte) (err error) { func (k *jsonWebKeySet) UnmarshalJSON(data []byte) (err error) {
var raw rawJSONWebKeySet var raw rawJSONWebKeySet
err = json.Unmarshal(data, &raw) err = json.Unmarshal(data, &raw)

View file

@ -23,53 +23,49 @@ const (
pkceCode = "pkce" pkceCode = "pkce"
) )
var ( var ErrUserInfoSubNotMatching = errors.New("sub from userinfo does not match the sub from the id_token")
ErrUserInfoSubNotMatching = errors.New("sub from userinfo does not match the sub from the id_token")
)
//RelyingParty declares the minimal interface for oidc clients // RelyingParty declares the minimal interface for oidc clients
type RelyingParty interface { type RelyingParty interface {
//OAuthConfig returns the oauth2 Config // OAuthConfig returns the oauth2 Config
OAuthConfig() *oauth2.Config OAuthConfig() *oauth2.Config
//Issuer returns the issuer of the oidc config // Issuer returns the issuer of the oidc config
Issuer() string Issuer() string
//IsPKCE returns if authorization is done using `Authorization Code Flow with Proof Key for Code Exchange (PKCE)` // IsPKCE returns if authorization is done using `Authorization Code Flow with Proof Key for Code Exchange (PKCE)`
IsPKCE() bool IsPKCE() bool
//CookieHandler returns a http cookie handler used for various state transfer cookies // CookieHandler returns a http cookie handler used for various state transfer cookies
CookieHandler() *httphelper.CookieHandler CookieHandler() *httphelper.CookieHandler
//HttpClient returns a http client used for calls to the openid provider, e.g. calling token endpoint // HttpClient returns a http client used for calls to the openid provider, e.g. calling token endpoint
HttpClient() *http.Client HttpClient() *http.Client
//IsOAuth2Only specifies whether relaying party handles only oauth2 or oidc calls // IsOAuth2Only specifies whether relaying party handles only oauth2 or oidc calls
IsOAuth2Only() bool IsOAuth2Only() bool
//Signer is used if the relaying party uses the JWT Profile // Signer is used if the relaying party uses the JWT Profile
Signer() jose.Signer Signer() jose.Signer
//GetEndSessionEndpoint returns the endpoint to sign out on a IDP // GetEndSessionEndpoint returns the endpoint to sign out on a IDP
GetEndSessionEndpoint() string GetEndSessionEndpoint() string
//UserinfoEndpoint returns the userinfo // UserinfoEndpoint returns the userinfo
UserinfoEndpoint() string UserinfoEndpoint() string
//IDTokenVerifier returns the verifier interface used for oidc id_token verification // IDTokenVerifier returns the verifier interface used for oidc id_token verification
IDTokenVerifier() IDTokenVerifier IDTokenVerifier() IDTokenVerifier
//ErrorHandler returns the handler used for callback errors // ErrorHandler returns the handler used for callback errors
ErrorHandler() func(http.ResponseWriter, *http.Request, string, string, string) ErrorHandler() func(http.ResponseWriter, *http.Request, string, string, string)
} }
type ErrorHandler func(w http.ResponseWriter, r *http.Request, errorType string, errorDesc string, state string) type ErrorHandler func(w http.ResponseWriter, r *http.Request, errorType string, errorDesc string, state string)
var ( var DefaultErrorHandler ErrorHandler = func(w http.ResponseWriter, r *http.Request, errorType string, errorDesc string, state string) {
DefaultErrorHandler ErrorHandler = func(w http.ResponseWriter, r *http.Request, errorType string, errorDesc string, state string) { http.Error(w, errorType+": "+errorDesc, http.StatusInternalServerError)
http.Error(w, errorType+": "+errorDesc, http.StatusInternalServerError) }
}
)
type relyingParty struct { type relyingParty struct {
issuer string issuer string
@ -138,9 +134,9 @@ func (rp *relyingParty) ErrorHandler() func(http.ResponseWriter, *http.Request,
return rp.errorHandler return rp.errorHandler
} }
//NewRelyingPartyOAuth creates an (OAuth2) RelyingParty with the given // NewRelyingPartyOAuth creates an (OAuth2) RelyingParty with the given
//OAuth2 Config and possible configOptions // OAuth2 Config and possible configOptions
//it will use the AuthURL and TokenURL set in config // it will use the AuthURL and TokenURL set in config
func NewRelyingPartyOAuth(config *oauth2.Config, options ...Option) (RelyingParty, error) { func NewRelyingPartyOAuth(config *oauth2.Config, options ...Option) (RelyingParty, error) {
rp := &relyingParty{ rp := &relyingParty{
oauthConfig: config, oauthConfig: config,
@ -161,9 +157,9 @@ func NewRelyingPartyOAuth(config *oauth2.Config, options ...Option) (RelyingPart
return rp, nil return rp, nil
} }
//NewRelyingPartyOIDC creates an (OIDC) RelyingParty with the given // NewRelyingPartyOIDC creates an (OIDC) RelyingParty with the given
//issuer, clientID, clientSecret, redirectURI, scopes and possible configOptions // issuer, clientID, clientSecret, redirectURI, scopes and possible configOptions
//it will run discovery on the provided issuer and use the found endpoints // it will run discovery on the provided issuer and use the found endpoints
func NewRelyingPartyOIDC(issuer, clientID, clientSecret, redirectURI string, scopes []string, options ...Option) (RelyingParty, error) { func NewRelyingPartyOIDC(issuer, clientID, clientSecret, redirectURI string, scopes []string, options ...Option) (RelyingParty, error) {
rp := &relyingParty{ rp := &relyingParty{
issuer: issuer, issuer: issuer,
@ -197,7 +193,7 @@ func NewRelyingPartyOIDC(issuer, clientID, clientSecret, redirectURI string, sco
return rp, nil return rp, nil
} }
//Option is the type for providing dynamic options to the relyingParty // Option is the type for providing dynamic options to the relyingParty
type Option func(*relyingParty) error type Option func(*relyingParty) error
func WithCustomDiscoveryUrl(url string) Option { func WithCustomDiscoveryUrl(url string) Option {
@ -207,7 +203,7 @@ func WithCustomDiscoveryUrl(url string) Option {
} }
} }
//WithCookieHandler set a `CookieHandler` for securing the various redirects // WithCookieHandler set a `CookieHandler` for securing the various redirects
func WithCookieHandler(cookieHandler *httphelper.CookieHandler) Option { func WithCookieHandler(cookieHandler *httphelper.CookieHandler) Option {
return func(rp *relyingParty) error { return func(rp *relyingParty) error {
rp.cookieHandler = cookieHandler rp.cookieHandler = cookieHandler
@ -215,9 +211,9 @@ func WithCookieHandler(cookieHandler *httphelper.CookieHandler) Option {
} }
} }
//WithPKCE sets the RP to use PKCE (oauth2 code challenge) // WithPKCE sets the RP to use PKCE (oauth2 code challenge)
//it also sets a `CookieHandler` for securing the various redirects // it also sets a `CookieHandler` for securing the various redirects
//and exchanging the code challenge // and exchanging the code challenge
func WithPKCE(cookieHandler *httphelper.CookieHandler) Option { func WithPKCE(cookieHandler *httphelper.CookieHandler) Option {
return func(rp *relyingParty) error { return func(rp *relyingParty) error {
rp.pkce = true rp.pkce = true
@ -226,7 +222,7 @@ func WithPKCE(cookieHandler *httphelper.CookieHandler) Option {
} }
} }
//WithHTTPClient provides the ability to set an http client to be used for the relaying party and verifier // WithHTTPClient provides the ability to set an http client to be used for the relaying party and verifier
func WithHTTPClient(client *http.Client) Option { func WithHTTPClient(client *http.Client) Option {
return func(rp *relyingParty) error { return func(rp *relyingParty) error {
rp.httpClient = client rp.httpClient = client
@ -297,7 +293,7 @@ func SignerFromKeyAndKeyID(key []byte, keyID string) SignerFromKey {
} }
} }
//Discover calls the discovery endpoint of the provided issuer and returns the found endpoints // Discover calls the discovery endpoint of the provided issuer and returns the found endpoints
// //
//deprecated: use client.Discover //deprecated: use client.Discover
func Discover(issuer string, httpClient *http.Client) (Endpoints, error) { func Discover(issuer string, httpClient *http.Client) (Endpoints, error) {
@ -317,7 +313,7 @@ func Discover(issuer string, httpClient *http.Client) (Endpoints, error) {
return GetEndpoints(discoveryConfig), nil return GetEndpoints(discoveryConfig), nil
} }
//AuthURL returns the auth request url // AuthURL returns the auth request url
//(wrapping the oauth2 `AuthCodeURL`) //(wrapping the oauth2 `AuthCodeURL`)
func AuthURL(state string, rp RelyingParty, opts ...AuthURLOpt) string { func AuthURL(state string, rp RelyingParty, opts ...AuthURLOpt) string {
authOpts := make([]oauth2.AuthCodeOption, 0) authOpts := make([]oauth2.AuthCodeOption, 0)
@ -327,8 +323,8 @@ func AuthURL(state string, rp RelyingParty, opts ...AuthURLOpt) string {
return rp.OAuthConfig().AuthCodeURL(state, authOpts...) return rp.OAuthConfig().AuthCodeURL(state, authOpts...)
} }
//AuthURLHandler extends the `AuthURL` method with a http redirect handler // AuthURLHandler extends the `AuthURL` method with a http redirect handler
//including handling setting cookie for secure `state` transfer // including handling setting cookie for secure `state` transfer
func AuthURLHandler(stateFn func() string, rp RelyingParty) http.HandlerFunc { func AuthURLHandler(stateFn func() string, rp RelyingParty) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
opts := make([]AuthURLOpt, 0) opts := make([]AuthURLOpt, 0)
@ -349,7 +345,7 @@ func AuthURLHandler(stateFn func() string, rp RelyingParty) http.HandlerFunc {
} }
} }
//GenerateAndStoreCodeChallenge generates a PKCE code challenge and stores its verifier into a secure cookie // GenerateAndStoreCodeChallenge generates a PKCE code challenge and stores its verifier into a secure cookie
func GenerateAndStoreCodeChallenge(w http.ResponseWriter, rp RelyingParty) (string, error) { func GenerateAndStoreCodeChallenge(w http.ResponseWriter, rp RelyingParty) (string, error) {
codeVerifier := base64.RawURLEncoding.EncodeToString([]byte(uuid.New().String())) codeVerifier := base64.RawURLEncoding.EncodeToString([]byte(uuid.New().String()))
if err := rp.CookieHandler().SetCookie(w, pkceCode, codeVerifier); err != nil { if err := rp.CookieHandler().SetCookie(w, pkceCode, codeVerifier); err != nil {
@ -358,8 +354,8 @@ func GenerateAndStoreCodeChallenge(w http.ResponseWriter, rp RelyingParty) (stri
return oidc.NewSHACodeChallenge(codeVerifier), nil return oidc.NewSHACodeChallenge(codeVerifier), nil
} }
//CodeExchange handles the oauth2 code exchange, extracting and validating the id_token // CodeExchange handles the oauth2 code exchange, extracting and validating the id_token
//returning it parsed together with the oauth2 tokens (access, refresh) // returning it parsed together with the oauth2 tokens (access, refresh)
func CodeExchange(ctx context.Context, code string, rp RelyingParty, opts ...CodeExchangeOpt) (tokens *oidc.Tokens, err error) { func CodeExchange(ctx context.Context, code string, rp RelyingParty, opts ...CodeExchangeOpt) (tokens *oidc.Tokens, err error) {
ctx = context.WithValue(ctx, oauth2.HTTPClient, rp.HttpClient()) ctx = context.WithValue(ctx, oauth2.HTTPClient, rp.HttpClient())
codeOpts := make([]oauth2.AuthCodeOption, 0) codeOpts := make([]oauth2.AuthCodeOption, 0)
@ -391,9 +387,9 @@ func CodeExchange(ctx context.Context, code string, rp RelyingParty, opts ...Cod
type CodeExchangeCallback func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp RelyingParty) type CodeExchangeCallback func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp RelyingParty)
//CodeExchangeHandler extends the `CodeExchange` method with a http handler // CodeExchangeHandler extends the `CodeExchange` method with a http handler
//including cookie handling for secure `state` transfer // including cookie handling for secure `state` transfer
//and optional PKCE code verifier checking // and optional PKCE code verifier checking
func CodeExchangeHandler(callback CodeExchangeCallback, rp RelyingParty) http.HandlerFunc { func CodeExchangeHandler(callback CodeExchangeCallback, rp RelyingParty) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
state, err := tryReadStateCookie(w, r, rp) state, err := tryReadStateCookie(w, r, rp)
@ -434,9 +430,9 @@ func CodeExchangeHandler(callback CodeExchangeCallback, rp RelyingParty) http.Ha
type CodeExchangeUserinfoCallback func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, provider RelyingParty, info oidc.UserInfo) type CodeExchangeUserinfoCallback func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, provider RelyingParty, info oidc.UserInfo)
//UserinfoCallback wraps the callback function of the CodeExchangeHandler // UserinfoCallback wraps the callback function of the CodeExchangeHandler
//and calls the userinfo endpoint with the access token // and calls the userinfo endpoint with the access token
//on success it will pass the userinfo into its callback function as well // on success it will pass the userinfo into its callback function as well
func UserinfoCallback(f CodeExchangeUserinfoCallback) CodeExchangeCallback { func UserinfoCallback(f CodeExchangeUserinfoCallback) CodeExchangeCallback {
return func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp RelyingParty) { return func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp RelyingParty) {
info, err := Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.GetSubject(), rp) info, err := Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.GetSubject(), rp)
@ -448,7 +444,7 @@ func UserinfoCallback(f CodeExchangeUserinfoCallback) CodeExchangeCallback {
} }
} }
//Userinfo will call the OIDC Userinfo Endpoint with the provided token // Userinfo will call the OIDC Userinfo Endpoint with the provided token
func Userinfo(token, tokenType, subject string, rp RelyingParty) (oidc.UserInfo, error) { func Userinfo(token, tokenType, subject string, rp RelyingParty) (oidc.UserInfo, error) {
req, err := http.NewRequest("GET", rp.UserinfoEndpoint(), nil) req, err := http.NewRequest("GET", rp.UserinfoEndpoint(), nil)
if err != nil { if err != nil {
@ -512,7 +508,7 @@ func GetEndpoints(discoveryConfig *oidc.DiscoveryConfiguration) Endpoints {
type AuthURLOpt func() []oauth2.AuthCodeOption type AuthURLOpt func() []oauth2.AuthCodeOption
//WithCodeChallenge sets the `code_challenge` params in the auth request // WithCodeChallenge sets the `code_challenge` params in the auth request
func WithCodeChallenge(codeChallenge string) AuthURLOpt { func WithCodeChallenge(codeChallenge string) AuthURLOpt {
return func() []oauth2.AuthCodeOption { return func() []oauth2.AuthCodeOption {
return []oauth2.AuthCodeOption{ return []oauth2.AuthCodeOption{
@ -522,7 +518,7 @@ func WithCodeChallenge(codeChallenge string) AuthURLOpt {
} }
} }
//WithPrompt sets the `prompt` params in the auth request // WithPrompt sets the `prompt` params in the auth request
func WithPrompt(prompt ...string) AuthURLOpt { func WithPrompt(prompt ...string) AuthURLOpt {
return func() []oauth2.AuthCodeOption { return func() []oauth2.AuthCodeOption {
return []oauth2.AuthCodeOption{ return []oauth2.AuthCodeOption{
@ -533,14 +529,14 @@ func WithPrompt(prompt ...string) AuthURLOpt {
type CodeExchangeOpt func() []oauth2.AuthCodeOption type CodeExchangeOpt func() []oauth2.AuthCodeOption
//WithCodeVerifier sets the `code_verifier` param in the token request // WithCodeVerifier sets the `code_verifier` param in the token request
func WithCodeVerifier(codeVerifier string) CodeExchangeOpt { func WithCodeVerifier(codeVerifier string) CodeExchangeOpt {
return func() []oauth2.AuthCodeOption { return func() []oauth2.AuthCodeOption {
return []oauth2.AuthCodeOption{oauth2.SetAuthURLParam("code_verifier", codeVerifier)} return []oauth2.AuthCodeOption{oauth2.SetAuthURLParam("code_verifier", codeVerifier)}
} }
} }
//WithClientAssertionJWT sets the `client_assertion` param in the token request // WithClientAssertionJWT sets the `client_assertion` param in the token request
func WithClientAssertionJWT(clientAssertion string) CodeExchangeOpt { func WithClientAssertionJWT(clientAssertion string) CodeExchangeOpt {
return func() []oauth2.AuthCodeOption { return func() []oauth2.AuthCodeOption {
return client.ClientAssertionCodeOptions(clientAssertion) return client.ClientAssertionCodeOptions(clientAssertion)

View file

@ -8,20 +8,20 @@ import (
"github.com/zitadel/oidc/pkg/oidc/grants/tokenexchange" "github.com/zitadel/oidc/pkg/oidc/grants/tokenexchange"
) )
//TokenExchangeRP extends the `RelyingParty` interface for the *draft* oauth2 `Token Exchange` // TokenExchangeRP extends the `RelyingParty` interface for the *draft* oauth2 `Token Exchange`
type TokenExchangeRP interface { type TokenExchangeRP interface {
RelyingParty RelyingParty
//TokenExchange implement the `Token Exchange Grant` exchanging some token for an other // TokenExchange implement the `Token Exchange Grant` exchanging some token for an other
TokenExchange(context.Context, *tokenexchange.TokenExchangeRequest) (*oauth2.Token, error) TokenExchange(context.Context, *tokenexchange.TokenExchangeRequest) (*oauth2.Token, error)
} }
//DelegationTokenExchangeRP extends the `TokenExchangeRP` interface // DelegationTokenExchangeRP extends the `TokenExchangeRP` interface
//for the specific `delegation token` request // for the specific `delegation token` request
type DelegationTokenExchangeRP interface { type DelegationTokenExchangeRP interface {
TokenExchangeRP TokenExchangeRP
//DelegationTokenExchange implement the `Token Exchange Grant` // DelegationTokenExchange implement the `Token Exchange Grant`
//providing an access token in request for a `delegation` token for a given resource / audience // providing an access token in request for a `delegation` token for a given resource / audience
DelegationTokenExchange(context.Context, string, ...tokenexchange.TokenExchangeOption) (*oauth2.Token, error) DelegationTokenExchange(context.Context, string, ...tokenexchange.TokenExchangeOption) (*oauth2.Token, error)
} }

View file

@ -19,7 +19,7 @@ type IDTokenVerifier interface {
MaxAge() time.Duration MaxAge() time.Duration
} }
//VerifyTokens implement the Token Response Validation as defined in OIDC specification // VerifyTokens implement the Token Response Validation as defined in OIDC specification
//https://openid.net/specs/openid-connect-core-1_0.html#TokenResponseValidation //https://openid.net/specs/openid-connect-core-1_0.html#TokenResponseValidation
func VerifyTokens(ctx context.Context, accessToken, idTokenString string, v IDTokenVerifier) (oidc.IDTokenClaims, error) { func VerifyTokens(ctx context.Context, accessToken, idTokenString string, v IDTokenVerifier) (oidc.IDTokenClaims, error) {
idToken, err := VerifyIDToken(ctx, idTokenString, v) idToken, err := VerifyIDToken(ctx, idTokenString, v)
@ -32,7 +32,7 @@ func VerifyTokens(ctx context.Context, accessToken, idTokenString string, v IDTo
return idToken, nil return idToken, nil
} }
//VerifyIDToken validates the id token according to // VerifyIDToken validates the id token according to
//https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation //https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
func VerifyIDToken(ctx context.Context, token string, v IDTokenVerifier) (oidc.IDTokenClaims, error) { func VerifyIDToken(ctx context.Context, token string, v IDTokenVerifier) (oidc.IDTokenClaims, error) {
claims := oidc.EmptyIDTokenClaims() claims := oidc.EmptyIDTokenClaims()
@ -88,7 +88,7 @@ func VerifyIDToken(ctx context.Context, token string, v IDTokenVerifier) (oidc.I
return claims, nil return claims, nil
} }
//VerifyAccessToken validates the access token according to // VerifyAccessToken validates the access token according to
//https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowTokenValidation //https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowTokenValidation
func VerifyAccessToken(accessToken, atHash string, sigAlgorithm jose.SignatureAlgorithm) error { func VerifyAccessToken(accessToken, atHash string, sigAlgorithm jose.SignatureAlgorithm) error {
if atHash == "" { if atHash == "" {
@ -105,8 +105,8 @@ func VerifyAccessToken(accessToken, atHash string, sigAlgorithm jose.SignatureAl
return nil return nil
} }
//NewIDTokenVerifier returns an implementation of `IDTokenVerifier` // NewIDTokenVerifier returns an implementation of `IDTokenVerifier`
//for `VerifyTokens` and `VerifyIDToken` // for `VerifyTokens` and `VerifyIDToken`
func NewIDTokenVerifier(issuer, clientID string, keySet oidc.KeySet, options ...VerifierOption) IDTokenVerifier { func NewIDTokenVerifier(issuer, clientID string, keySet oidc.KeySet, options ...VerifierOption) IDTokenVerifier {
v := &idTokenVerifier{ v := &idTokenVerifier{
issuer: issuer, issuer: issuer,
@ -125,46 +125,46 @@ func NewIDTokenVerifier(issuer, clientID string, keySet oidc.KeySet, options ...
return v return v
} }
//VerifierOption is the type for providing dynamic options to the IDTokenVerifier // VerifierOption is the type for providing dynamic options to the IDTokenVerifier
type VerifierOption func(*idTokenVerifier) type VerifierOption func(*idTokenVerifier)
//WithIssuedAtOffset mitigates the risk of iat to be in the future // 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 // because of clock skews with the ability to add an offset to the current time
func WithIssuedAtOffset(offset time.Duration) func(*idTokenVerifier) { func WithIssuedAtOffset(offset time.Duration) func(*idTokenVerifier) {
return func(v *idTokenVerifier) { return func(v *idTokenVerifier) {
v.offset = offset v.offset = offset
} }
} }
//WithIssuedAtMaxAge provides the ability to define the maximum duration between iat and now // WithIssuedAtMaxAge provides the ability to define the maximum duration between iat and now
func WithIssuedAtMaxAge(maxAge time.Duration) func(*idTokenVerifier) { func WithIssuedAtMaxAge(maxAge time.Duration) func(*idTokenVerifier) {
return func(v *idTokenVerifier) { return func(v *idTokenVerifier) {
v.maxAge = maxAge v.maxAge = maxAge
} }
} }
//WithNonce sets the function to check the nonce // WithNonce sets the function to check the nonce
func WithNonce(nonce func(context.Context) string) VerifierOption { func WithNonce(nonce func(context.Context) string) VerifierOption {
return func(v *idTokenVerifier) { return func(v *idTokenVerifier) {
v.nonce = nonce v.nonce = nonce
} }
} }
//WithACRVerifier sets the verifier for the acr claim // WithACRVerifier sets the verifier for the acr claim
func WithACRVerifier(verifier oidc.ACRVerifier) VerifierOption { func WithACRVerifier(verifier oidc.ACRVerifier) VerifierOption {
return func(v *idTokenVerifier) { return func(v *idTokenVerifier) {
v.acr = verifier v.acr = verifier
} }
} }
//WithAuthTimeMaxAge provides the ability to define the maximum duration between auth_time and now // WithAuthTimeMaxAge provides the ability to define the maximum duration between auth_time and now
func WithAuthTimeMaxAge(maxAge time.Duration) VerifierOption { func WithAuthTimeMaxAge(maxAge time.Duration) VerifierOption {
return func(v *idTokenVerifier) { return func(v *idTokenVerifier) {
v.maxAge = maxAge v.maxAge = maxAge
} }
} }
//WithSupportedSigningAlgorithms overwrites the default RS256 signing algorithm // WithSupportedSigningAlgorithms overwrites the default RS256 signing algorithm
func WithSupportedSigningAlgorithms(algs ...string) VerifierOption { func WithSupportedSigningAlgorithms(algs ...string) VerifierOption {
return func(v *idTokenVerifier) { return func(v *idTokenVerifier) {
v.supportedSignAlgs = algs v.supportedSignAlgs = algs

View file

@ -43,6 +43,7 @@ func NewResourceServerClientCredentials(issuer, clientID, clientSecret string, o
} }
return newResourceServer(issuer, authorizer, option...) return newResourceServer(issuer, authorizer, option...)
} }
func NewResourceServerJWTProfile(issuer, clientID, keyID string, key []byte, options ...Option) (ResourceServer, error) { func NewResourceServerJWTProfile(issuer, clientID, keyID string, key []byte, options ...Option) (ResourceServer, error) {
signer, err := client.NewSignerFromPrivateKeyByte(key, keyID) signer, err := client.NewSignerFromPrivateKeyByte(key, keyID)
if err != nil { if err != nil {
@ -91,14 +92,14 @@ func NewResourceServerFromKeyFile(issuer, path string, options ...Option) (Resou
type Option func(*resourceServer) type Option func(*resourceServer)
//WithClient provides the ability to set an http client to be used for the resource server // WithClient provides the ability to set an http client to be used for the resource server
func WithClient(client *http.Client) Option { func WithClient(client *http.Client) Option {
return func(server *resourceServer) { return func(server *resourceServer) {
server.httpClient = client server.httpClient = client
} }
} }
//WithStaticEndpoints provides the ability to set static token and introspect URL // WithStaticEndpoints provides the ability to set static token and introspect URL
func WithStaticEndpoints(tokenURL, introspectURL string) Option { func WithStaticEndpoints(tokenURL, introspectURL string) Option {
return func(server *resourceServer) { return func(server *resourceServer) {
server.tokenURL = tokenURL server.tokenURL = tokenURL

View file

@ -9,9 +9,7 @@ import (
"io" "io"
) )
var ( var ErrCipherTextBlockSize = errors.New("ciphertext block size is too short")
ErrCipherTextBlockSize = errors.New("ciphertext block size is too short")
)
func EncryptAES(data string, key string) (string, error) { func EncryptAES(data string, key string) (string, error) {
encrypted, err := EncryptBytesAES([]byte(data), key) encrypted, err := EncryptBytesAES([]byte(data), key)

View file

@ -11,9 +11,7 @@ import (
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
) )
var ( var ErrUnsupportedAlgorithm = errors.New("unsupported signing algorithm")
ErrUnsupportedAlgorithm = errors.New("unsupported signing algorithm")
)
func GetHashAlgorithm(sigAlgorithm jose.SignatureAlgorithm) (hash.Hash, error) { func GetHashAlgorithm(sigAlgorithm jose.SignatureAlgorithm) (hash.Hash, error) {
switch sigAlgorithm { switch sigAlgorithm {

View file

@ -12,15 +12,14 @@ import (
"time" "time"
) )
var ( var DefaultHTTPClient = &http.Client{
DefaultHTTPClient = &http.Client{ Timeout: 30 * time.Second,
Timeout: 30 * time.Second, }
}
)
type Decoder interface { type Decoder interface {
Decode(dst interface{}, src map[string][]string) error Decode(dst interface{}, src map[string][]string) error
} }
type Encoder interface { type Encoder interface {
Encode(src interface{}, dst map[string][]string) error Encode(src interface{}, dst map[string][]string) error
} }

View file

@ -1,40 +1,40 @@
package oidc package oidc
const ( const (
//ScopeOpenID defines the scope `openid` // ScopeOpenID defines the scope `openid`
//OpenID Connect requests MUST contain the `openid` scope value // OpenID Connect requests MUST contain the `openid` scope value
ScopeOpenID = "openid" ScopeOpenID = "openid"
//ScopeProfile defines the scope `profile` // ScopeProfile defines the scope `profile`
//This (optional) scope value requests access to the End-User's default profile Claims, // This (optional) scope value requests access to the End-User's default profile Claims,
//which are: name, family_name, given_name, middle_name, nickname, preferred_username, // which are: name, family_name, given_name, middle_name, nickname, preferred_username,
//profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at. // profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.
ScopeProfile = "profile" ScopeProfile = "profile"
//ScopeEmail defines the scope `email` // ScopeEmail defines the scope `email`
//This (optional) scope value requests access to the email and email_verified Claims. // This (optional) scope value requests access to the email and email_verified Claims.
ScopeEmail = "email" ScopeEmail = "email"
//ScopeAddress defines the scope `address` // ScopeAddress defines the scope `address`
//This (optional) scope value requests access to the address Claim. // This (optional) scope value requests access to the address Claim.
ScopeAddress = "address" ScopeAddress = "address"
//ScopePhone defines the scope `phone` // ScopePhone defines the scope `phone`
//This (optional) scope value requests access to the phone_number and phone_number_verified Claims. // This (optional) scope value requests access to the phone_number and phone_number_verified Claims.
ScopePhone = "phone" ScopePhone = "phone"
//ScopeOfflineAccess defines the scope `offline_access` // ScopeOfflineAccess defines the scope `offline_access`
//This (optional) scope value requests that an OAuth 2.0 Refresh Token be issued that can be used to obtain an Access Token // This (optional) scope value requests that an OAuth 2.0 Refresh Token be issued that can be used to obtain an Access Token
//that grants access to the End-User's UserInfo Endpoint even when the End-User is not present (not logged in). // that grants access to the End-User's UserInfo Endpoint even when the End-User is not present (not logged in).
ScopeOfflineAccess = "offline_access" ScopeOfflineAccess = "offline_access"
//ResponseTypeCode for the Authorization Code Flow returning a code from the Authorization Server // ResponseTypeCode for the Authorization Code Flow returning a code from the Authorization Server
ResponseTypeCode ResponseType = "code" ResponseTypeCode ResponseType = "code"
//ResponseTypeIDToken for the Implicit Flow returning id and access tokens directly from the Authorization Server // ResponseTypeIDToken for the Implicit Flow returning id and access tokens directly from the Authorization Server
ResponseTypeIDToken ResponseType = "id_token token" ResponseTypeIDToken ResponseType = "id_token token"
//ResponseTypeIDTokenOnly for the Implicit Flow returning only id token directly from the Authorization Server // ResponseTypeIDTokenOnly for the Implicit Flow returning only id token directly from the Authorization Server
ResponseTypeIDTokenOnly ResponseType = "id_token" ResponseTypeIDTokenOnly ResponseType = "id_token"
DisplayPage Display = "page" DisplayPage Display = "page"
@ -45,21 +45,21 @@ const (
ResponseModeQuery ResponseMode = "query" ResponseModeQuery ResponseMode = "query"
ResponseModeFragment ResponseMode = "fragment" ResponseModeFragment ResponseMode = "fragment"
//PromptNone (`none`) disallows the Authorization Server to display any authentication or consent user interface pages. // PromptNone (`none`) disallows the Authorization Server to display any authentication or consent user interface pages.
//An error (login_required, interaction_required, ...) will be returned if the user is not already authenticated or consent is needed // An error (login_required, interaction_required, ...) will be returned if the user is not already authenticated or consent is needed
PromptNone = "none" PromptNone = "none"
//PromptLogin (`login`) directs the Authorization Server to prompt the End-User for reauthentication. // PromptLogin (`login`) directs the Authorization Server to prompt the End-User for reauthentication.
PromptLogin = "login" PromptLogin = "login"
//PromptConsent (`consent`) directs the Authorization Server to prompt the End-User for consent (of sharing information). // PromptConsent (`consent`) directs the Authorization Server to prompt the End-User for consent (of sharing information).
PromptConsent = "consent" PromptConsent = "consent"
//PromptSelectAccount (`select_account `) directs the Authorization Server to prompt the End-User to select a user account (to enable multi user / session switching) // PromptSelectAccount (`select_account `) directs the Authorization Server to prompt the End-User to select a user account (to enable multi user / session switching)
PromptSelectAccount = "select_account" PromptSelectAccount = "select_account"
) )
//AuthRequest according to: // AuthRequest according to:
//https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest //https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
type AuthRequest struct { type AuthRequest struct {
Scopes SpaceDelimitedArray `json:"scope" schema:"scope"` Scopes SpaceDelimitedArray `json:"scope" schema:"scope"`
@ -82,21 +82,21 @@ type AuthRequest struct {
CodeChallenge string `json:"code_challenge" schema:"code_challenge"` CodeChallenge string `json:"code_challenge" schema:"code_challenge"`
CodeChallengeMethod CodeChallengeMethod `json:"code_challenge_method" schema:"code_challenge_method"` CodeChallengeMethod CodeChallengeMethod `json:"code_challenge_method" schema:"code_challenge_method"`
//RequestParam enables OIDC requests to be passed in a single, self-contained parameter (as JWT, called Request Object) // RequestParam enables OIDC requests to be passed in a single, self-contained parameter (as JWT, called Request Object)
RequestParam string `schema:"request"` RequestParam string `schema:"request"`
} }
//GetRedirectURI returns the redirect_uri value for the ErrAuthRequest interface // GetRedirectURI returns the redirect_uri value for the ErrAuthRequest interface
func (a *AuthRequest) GetRedirectURI() string { func (a *AuthRequest) GetRedirectURI() string {
return a.RedirectURI return a.RedirectURI
} }
//GetResponseType returns the response_type value for the ErrAuthRequest interface // GetResponseType returns the response_type value for the ErrAuthRequest interface
func (a *AuthRequest) GetResponseType() ResponseType { func (a *AuthRequest) GetResponseType() ResponseType {
return a.ResponseType return a.ResponseType
} }
//GetState returns the optional state value for the ErrAuthRequest interface // GetState returns the optional state value for the ErrAuthRequest interface
func (a *AuthRequest) GetState() string { func (a *AuthRequest) GetState() string {
return a.State return a.State
} }

View file

@ -9,143 +9,143 @@ const (
) )
type DiscoveryConfiguration struct { type DiscoveryConfiguration struct {
//Issuer is the identifier of the OP and is used in the tokens as `iss` claim. // Issuer is the identifier of the OP and is used in the tokens as `iss` claim.
Issuer string `json:"issuer,omitempty"` Issuer string `json:"issuer,omitempty"`
//AuthorizationEndpoint is the URL of the OAuth 2.0 Authorization Endpoint where all user interactive login start // AuthorizationEndpoint is the URL of the OAuth 2.0 Authorization Endpoint where all user interactive login start
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"` AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
//TokenEndpoint is the URL of the OAuth 2.0 Token Endpoint where all tokens are issued, except when using Implicit Flow // TokenEndpoint is the URL of the OAuth 2.0 Token Endpoint where all tokens are issued, except when using Implicit Flow
TokenEndpoint string `json:"token_endpoint,omitempty"` TokenEndpoint string `json:"token_endpoint,omitempty"`
//IntrospectionEndpoint is the URL of the OAuth 2.0 Introspection Endpoint. // IntrospectionEndpoint is the URL of the OAuth 2.0 Introspection Endpoint.
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"` IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
//UserinfoEndpoint is the URL where an access_token can be used to retrieve the Userinfo. // UserinfoEndpoint is the URL where an access_token can be used to retrieve the Userinfo.
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"` UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
//RevocationEndpoint is the URL of the OAuth 2.0 Revocation Endpoint. // RevocationEndpoint is the URL of the OAuth 2.0 Revocation Endpoint.
RevocationEndpoint string `json:"revocation_endpoint,omitempty"` RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
//EndSessionEndpoint is a URL where the RP can perform a redirect to request that the End-User be logged out at the OP. // EndSessionEndpoint is a URL where the RP can perform a redirect to request that the End-User be logged out at the OP.
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"` EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
//CheckSessionIframe is a URL where the OP provides an iframe that support cross-origin communications for session state information with the RP Client. // CheckSessionIframe is a URL where the OP provides an iframe that support cross-origin communications for session state information with the RP Client.
CheckSessionIframe string `json:"check_session_iframe,omitempty"` CheckSessionIframe string `json:"check_session_iframe,omitempty"`
//JwksURI is the URL of the JSON Web Key Set. This site contains the signing keys that RPs can use to validate the signature. // JwksURI is the URL of the JSON Web Key Set. This site contains the signing keys that RPs can use to validate the signature.
//It may also contain the OP's encryption keys that RPs can use to encrypt request to the OP. // It may also contain the OP's encryption keys that RPs can use to encrypt request to the OP.
JwksURI string `json:"jwks_uri,omitempty"` JwksURI string `json:"jwks_uri,omitempty"`
//RegistrationEndpoint is the URL for the Dynamic Client Registration. // RegistrationEndpoint is the URL for the Dynamic Client Registration.
RegistrationEndpoint string `json:"registration_endpoint,omitempty"` RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
//ScopesSupported lists an array of supported scopes. This list must not include every supported scope by the OP. // ScopesSupported lists an array of supported scopes. This list must not include every supported scope by the OP.
ScopesSupported []string `json:"scopes_supported,omitempty"` ScopesSupported []string `json:"scopes_supported,omitempty"`
//ResponseTypesSupported contains a list of the OAuth 2.0 response_type values that the OP supports (code, id_token, token id_token, ...). // ResponseTypesSupported contains a list of the OAuth 2.0 response_type values that the OP supports (code, id_token, token id_token, ...).
ResponseTypesSupported []string `json:"response_types_supported,omitempty"` ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
//ResponseModesSupported contains a list of the OAuth 2.0 response_mode values that the OP supports. If omitted, the default value is ["query", "fragment"]. // ResponseModesSupported contains a list of the OAuth 2.0 response_mode values that the OP supports. If omitted, the default value is ["query", "fragment"].
ResponseModesSupported []string `json:"response_modes_supported,omitempty"` ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
//GrantTypesSupported contains a list of the OAuth 2.0 grant_type values that the OP supports. If omitted, the default value is ["authorization_code", "implicit"]. // GrantTypesSupported contains a list of the OAuth 2.0 grant_type values that the OP supports. If omitted, the default value is ["authorization_code", "implicit"].
GrantTypesSupported []GrantType `json:"grant_types_supported,omitempty"` GrantTypesSupported []GrantType `json:"grant_types_supported,omitempty"`
//ACRValuesSupported contains a list of Authentication Context Class References that the OP supports. // ACRValuesSupported contains a list of Authentication Context Class References that the OP supports.
ACRValuesSupported []string `json:"acr_values_supported,omitempty"` ACRValuesSupported []string `json:"acr_values_supported,omitempty"`
//SubjectTypesSupported contains a list of Subject Identifier types that the OP supports (pairwise, public). // SubjectTypesSupported contains a list of Subject Identifier types that the OP supports (pairwise, public).
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"` SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
//IDTokenSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for the ID Token. // IDTokenSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for the ID Token.
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"` IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
//IDTokenEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for the ID Token. // IDTokenEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for the ID Token.
IDTokenEncryptionAlgValuesSupported []string `json:"id_token_encryption_alg_values_supported,omitempty"` IDTokenEncryptionAlgValuesSupported []string `json:"id_token_encryption_alg_values_supported,omitempty"`
//IDTokenEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for the ID Token. // IDTokenEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for the ID Token.
IDTokenEncryptionEncValuesSupported []string `json:"id_token_encryption_enc_values_supported,omitempty"` IDTokenEncryptionEncValuesSupported []string `json:"id_token_encryption_enc_values_supported,omitempty"`
//UserinfoSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for UserInfo Endpoint. // UserinfoSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for UserInfo Endpoint.
UserinfoSigningAlgValuesSupported []string `json:"userinfo_signing_alg_values_supported,omitempty"` UserinfoSigningAlgValuesSupported []string `json:"userinfo_signing_alg_values_supported,omitempty"`
//UserinfoEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for the UserInfo Endpoint. // UserinfoEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for the UserInfo Endpoint.
UserinfoEncryptionAlgValuesSupported []string `json:"userinfo_encryption_alg_values_supported,omitempty"` UserinfoEncryptionAlgValuesSupported []string `json:"userinfo_encryption_alg_values_supported,omitempty"`
//UserinfoEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for the UserInfo Endpoint. // UserinfoEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for the UserInfo Endpoint.
UserinfoEncryptionEncValuesSupported []string `json:"userinfo_encryption_enc_values_supported,omitempty"` UserinfoEncryptionEncValuesSupported []string `json:"userinfo_encryption_enc_values_supported,omitempty"`
//RequestObjectSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for Request Objects. // RequestObjectSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for Request Objects.
//These algorithms are used both then the Request Object is passed by value (using the request parameter) and when it is passed by reference (using the request_uri parameter). // These algorithms are used both then the Request Object is passed by value (using the request parameter) and when it is passed by reference (using the request_uri parameter).
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported,omitempty"` RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported,omitempty"`
//RequestObjectEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for Request Objects. // RequestObjectEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for Request Objects.
//These algorithms are used both when the Request Object is passed by value and by reference. // These algorithms are used both when the Request Object is passed by value and by reference.
RequestObjectEncryptionAlgValuesSupported []string `json:"request_object_encryption_alg_values_supported,omitempty"` RequestObjectEncryptionAlgValuesSupported []string `json:"request_object_encryption_alg_values_supported,omitempty"`
//RequestObjectEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for Request Objects. // RequestObjectEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for Request Objects.
//These algorithms are used both when the Request Object is passed by value and by reference. // These algorithms are used both when the Request Object is passed by value and by reference.
RequestObjectEncryptionEncValuesSupported []string `json:"request_object_encryption_enc_values_supported,omitempty"` RequestObjectEncryptionEncValuesSupported []string `json:"request_object_encryption_enc_values_supported,omitempty"`
//TokenEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Token Endpoint. If omitted, the default is client_secret_basic. // TokenEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Token Endpoint. If omitted, the default is client_secret_basic.
TokenEndpointAuthMethodsSupported []AuthMethod `json:"token_endpoint_auth_methods_supported,omitempty"` TokenEndpointAuthMethodsSupported []AuthMethod `json:"token_endpoint_auth_methods_supported,omitempty"`
//TokenEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Token Endpoint // TokenEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Token Endpoint
//for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt. // for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt.
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"` TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`
//RevocationEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Revocation Endpoint. If omitted, the default is client_secret_basic. // RevocationEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Revocation Endpoint. If omitted, the default is client_secret_basic.
RevocationEndpointAuthMethodsSupported []AuthMethod `json:"revocation_endpoint_auth_methods_supported,omitempty"` RevocationEndpointAuthMethodsSupported []AuthMethod `json:"revocation_endpoint_auth_methods_supported,omitempty"`
//RevocationEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Revocation Endpoint // RevocationEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Revocation Endpoint
//for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt. // for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt.
RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"` RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"`
//IntrospectionEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Introspection Endpoint. // IntrospectionEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Introspection Endpoint.
IntrospectionEndpointAuthMethodsSupported []AuthMethod `json:"introspection_endpoint_auth_methods_supported,omitempty"` IntrospectionEndpointAuthMethodsSupported []AuthMethod `json:"introspection_endpoint_auth_methods_supported,omitempty"`
//IntrospectionEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Revocation Endpoint // IntrospectionEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Revocation Endpoint
//for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt. // for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt.
IntrospectionEndpointAuthSigningAlgValuesSupported []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"` IntrospectionEndpointAuthSigningAlgValuesSupported []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"`
//DisplayValuesSupported contains a list of display parameter values that the OP supports (page, popup, touch, wap). // DisplayValuesSupported contains a list of display parameter values that the OP supports (page, popup, touch, wap).
DisplayValuesSupported []Display `json:"display_values_supported,omitempty"` DisplayValuesSupported []Display `json:"display_values_supported,omitempty"`
//ClaimTypesSupported contains a list of Claim Types that the OP supports (normal, aggregated, distributed). If omitted, the default is normal Claims. // ClaimTypesSupported contains a list of Claim Types that the OP supports (normal, aggregated, distributed). If omitted, the default is normal Claims.
ClaimTypesSupported []string `json:"claim_types_supported,omitempty"` ClaimTypesSupported []string `json:"claim_types_supported,omitempty"`
//ClaimsSupported contains a list of Claim Names the OP may be able to supply values for. This list might not be exhaustive. // ClaimsSupported contains a list of Claim Names the OP may be able to supply values for. This list might not be exhaustive.
ClaimsSupported []string `json:"claims_supported,omitempty"` ClaimsSupported []string `json:"claims_supported,omitempty"`
//ClaimsParameterSupported specifies whether the OP supports use of the `claims` parameter. If omitted, the default is false. // ClaimsParameterSupported specifies whether the OP supports use of the `claims` parameter. If omitted, the default is false.
ClaimsParameterSupported bool `json:"claims_parameter_supported,omitempty"` ClaimsParameterSupported bool `json:"claims_parameter_supported,omitempty"`
//CodeChallengeMethodsSupported contains a list of Proof Key for Code Exchange (PKCE) code challenge methods supported by the OP. // CodeChallengeMethodsSupported contains a list of Proof Key for Code Exchange (PKCE) code challenge methods supported by the OP.
CodeChallengeMethodsSupported []CodeChallengeMethod `json:"code_challenge_methods_supported,omitempty"` CodeChallengeMethodsSupported []CodeChallengeMethod `json:"code_challenge_methods_supported,omitempty"`
//ServiceDocumentation is a URL where developers can get information about the OP and its usage. // ServiceDocumentation is a URL where developers can get information about the OP and its usage.
ServiceDocumentation string `json:"service_documentation,omitempty"` ServiceDocumentation string `json:"service_documentation,omitempty"`
//ClaimsLocalesSupported contains a list of BCP47 language tag values that the OP supports for values of Claims returned. // ClaimsLocalesSupported contains a list of BCP47 language tag values that the OP supports for values of Claims returned.
ClaimsLocalesSupported []language.Tag `json:"claims_locales_supported,omitempty"` ClaimsLocalesSupported []language.Tag `json:"claims_locales_supported,omitempty"`
//UILocalesSupported contains a list of BCP47 language tag values that the OP supports for the user interface. // UILocalesSupported contains a list of BCP47 language tag values that the OP supports for the user interface.
UILocalesSupported []language.Tag `json:"ui_locales_supported,omitempty"` UILocalesSupported []language.Tag `json:"ui_locales_supported,omitempty"`
//RequestParameterSupported specifies whether the OP supports use of the `request` parameter. If omitted, the default value is false. // RequestParameterSupported specifies whether the OP supports use of the `request` parameter. If omitted, the default value is false.
RequestParameterSupported bool `json:"request_parameter_supported,omitempty"` RequestParameterSupported bool `json:"request_parameter_supported,omitempty"`
//RequestURIParameterSupported specifies whether the OP supports use of the `request_uri` parameter. If omitted, the default value is true. (therefore no omitempty) // RequestURIParameterSupported specifies whether the OP supports use of the `request_uri` parameter. If omitted, the default value is true. (therefore no omitempty)
RequestURIParameterSupported bool `json:"request_uri_parameter_supported"` RequestURIParameterSupported bool `json:"request_uri_parameter_supported"`
//RequireRequestURIRegistration specifies whether the OP requires any `request_uri` to be pre-registered using the request_uris registration parameter. If omitted, the default value is false. // RequireRequestURIRegistration specifies whether the OP requires any `request_uri` to be pre-registered using the request_uris registration parameter. If omitted, the default value is false.
RequireRequestURIRegistration bool `json:"require_request_uri_registration,omitempty"` RequireRequestURIRegistration bool `json:"require_request_uri_registration,omitempty"`
//OPPolicyURI is a URL the OP provides to the person registering the Client to read about the OP's requirements on how the RP can use the data provided by the OP. // OPPolicyURI is a URL the OP provides to the person registering the Client to read about the OP's requirements on how the RP can use the data provided by the OP.
OPPolicyURI string `json:"op_policy_uri,omitempty"` OPPolicyURI string `json:"op_policy_uri,omitempty"`
//OPTermsOfServiceURI is a URL the OpenID Provider provides to the person registering the Client to read about OpenID Provider's terms of service. // OPTermsOfServiceURI is a URL the OpenID Provider provides to the person registering the Client to read about OpenID Provider's terms of service.
OPTermsOfServiceURI string `json:"op_tos_uri,omitempty"` OPTermsOfServiceURI string `json:"op_tos_uri,omitempty"`
} }

View file

@ -13,8 +13,8 @@ type clientCredentialsGrant struct {
clientSecret string `schema:"client_secret"` clientSecret string `schema:"client_secret"`
} }
//ClientCredentialsGrantBasic creates an oauth2 `Client Credentials` Grant // ClientCredentialsGrantBasic creates an oauth2 `Client Credentials` Grant
//sending client_id and client_secret as basic auth header // sending client_id and client_secret as basic auth header
func ClientCredentialsGrantBasic(scopes ...string) *clientCredentialsGrantBasic { func ClientCredentialsGrantBasic(scopes ...string) *clientCredentialsGrantBasic {
return &clientCredentialsGrantBasic{ return &clientCredentialsGrantBasic{
grantType: "client_credentials", grantType: "client_credentials",
@ -22,8 +22,8 @@ func ClientCredentialsGrantBasic(scopes ...string) *clientCredentialsGrantBasic
} }
} }
//ClientCredentialsGrantValues creates an oauth2 `Client Credentials` Grant // ClientCredentialsGrantValues creates an oauth2 `Client Credentials` Grant
//sending client_id and client_secret as form values // sending client_id and client_secret as form values
func ClientCredentialsGrantValues(clientID, clientSecret string, scopes ...string) *clientCredentialsGrant { func ClientCredentialsGrantValues(clientID, clientSecret string, scopes ...string) *clientCredentialsGrant {
return &clientCredentialsGrant{ return &clientCredentialsGrant{
clientCredentialsGrantBasic: ClientCredentialsGrantBasic(scopes...), clientCredentialsGrantBasic: ClientCredentialsGrantBasic(scopes...),

View file

@ -6,9 +6,9 @@ type JWTProfileGrantRequest struct {
GrantType GrantType `schema:"grant_type"` GrantType GrantType `schema:"grant_type"`
} }
//NewJWTProfileGrantRequest creates an oauth2 `JSON Web Token (JWT) Profile` Grant // NewJWTProfileGrantRequest creates an oauth2 `JSON Web Token (JWT) Profile` Grant
//`urn:ietf:params:oauth:grant-type:jwt-bearer` //`urn:ietf:params:oauth:grant-type:jwt-bearer`
//sending a self-signed jwt as assertion // sending a self-signed jwt as assertion
func NewJWTProfileGrantRequest(assertion string, scopes ...string) *JWTProfileGrantRequest { func NewJWTProfileGrantRequest(assertion string, scopes ...string) *JWTProfileGrantRequest {
return &JWTProfileGrantRequest{ return &JWTProfileGrantRequest{
GrantType: GrantTypeBearer, GrantType: GrantTypeBearer,

View file

@ -19,16 +19,16 @@ var (
ErrKeyNone = errors.New("no possible keys matches") ErrKeyNone = errors.New("no possible keys matches")
) )
//KeySet represents a set of JSON Web Keys // KeySet represents a set of JSON Web Keys
// - remotely fetch via discovery and jwks_uri -> `remoteKeySet` // - remotely fetch via discovery and jwks_uri -> `remoteKeySet`
// - held by the OP itself in storage -> `openIDKeySet` // - held by the OP itself in storage -> `openIDKeySet`
// - dynamically aggregated by request for OAuth JWT Profile Assertion -> `jwtProfileKeySet` // - dynamically aggregated by request for OAuth JWT Profile Assertion -> `jwtProfileKeySet`
type KeySet interface { type KeySet interface {
//VerifySignature verifies the signature with the given keyset and returns the raw payload // VerifySignature verifies the signature with the given keyset and returns the raw payload
VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error)
} }
//GetKeyIDAndAlg returns the `kid` and `alg` claim from the JWS header // GetKeyIDAndAlg returns the `kid` and `alg` claim from the JWS header
func GetKeyIDAndAlg(jws *jose.JSONWebSignature) (string, string) { func GetKeyIDAndAlg(jws *jose.JSONWebSignature) (string, string) {
keyID := "" keyID := ""
alg := "" alg := ""
@ -40,11 +40,11 @@ func GetKeyIDAndAlg(jws *jose.JSONWebSignature) (string, string) {
return keyID, alg return keyID, alg
} }
//FindKey searches the given JSON Web Keys for the requested key ID, usage and key type // FindKey searches the given JSON Web Keys for the requested key ID, usage and key type
// //
//will return the key immediately if matches exact (id, usage, type) // will return the key immediately if matches exact (id, usage, type)
// //
//will return false none or multiple match // will return false none or multiple match
// //
//deprecated: use FindMatchingKey which will return an error (more specific) instead of just a bool //deprecated: use FindMatchingKey which will return an error (more specific) instead of just a bool
//moved implementation already to FindMatchingKey //moved implementation already to FindMatchingKey
@ -53,35 +53,35 @@ func FindKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (jose.JSON
return key, err == nil return key, err == nil
} }
//FindMatchingKey searches the given JSON Web Keys for the requested key ID, usage and alg type // FindMatchingKey searches the given JSON Web Keys for the requested key ID, usage and alg type
// //
//will return the key immediately if matches exact (id, usage, type) // will return the key immediately if matches exact (id, usage, type)
// //
//will return a specific error if none (ErrKeyNone) or multiple (ErrKeyMultiple) match // will return a specific error if none (ErrKeyNone) or multiple (ErrKeyMultiple) match
func FindMatchingKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (key jose.JSONWebKey, err error) { func FindMatchingKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (key jose.JSONWebKey, err error) {
var validKeys []jose.JSONWebKey var validKeys []jose.JSONWebKey
for _, k := range keys { for _, k := range keys {
//ignore all keys with wrong use (let empty use of published key pass) // ignore all keys with wrong use (let empty use of published key pass)
if k.Use != use && k.Use != "" { if k.Use != use && k.Use != "" {
continue continue
} }
//ignore all keys with wrong algorithm type // ignore all keys with wrong algorithm type
if !algToKeyType(k.Key, expectedAlg) { if !algToKeyType(k.Key, expectedAlg) {
continue continue
} }
//if we get here, use and alg match, so an equal (not empty) keyID is an exact match // if we get here, use and alg match, so an equal (not empty) keyID is an exact match
if k.KeyID == keyID && keyID != "" { if k.KeyID == keyID && keyID != "" {
return k, nil return k, nil
} }
//keyIDs did not match or at least one was empty (if later, then it could be a match) // keyIDs did not match or at least one was empty (if later, then it could be a match)
if k.KeyID == "" || keyID == "" { if k.KeyID == "" || keyID == "" {
validKeys = append(validKeys, k) validKeys = append(validKeys, k)
} }
} }
//if we get here, no match was possible at all (use / alg) or no exact match due to // if we get here, no match was possible at all (use / alg) or no exact match due to
//the signed JWT and / or the published keys didn't have a kid // the signed JWT and / or the published keys didn't have a kid
//if later applies and only one key could be found, we'll return it // if later applies and only one key could be found, we'll return it
//otherwise a corresponding error will be thrown // otherwise a corresponding error will be thrown
if len(validKeys) == 1 { if len(validKeys) == 1 {
return validKeys[0], nil return validKeys[0], nil
} }

View file

@ -1,6 +1,6 @@
package oidc package oidc
//EndSessionRequest for the RP-Initiated Logout according to: // EndSessionRequest for the RP-Initiated Logout according to:
//https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout //https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout
type EndSessionRequest struct { type EndSessionRequest struct {
IdTokenHint string `schema:"id_token_hint"` IdTokenHint string `schema:"id_token_hint"`

View file

@ -14,7 +14,7 @@ import (
) )
const ( const (
//BearerToken defines the token_type `Bearer`, which is returned in a successful token response // BearerToken defines the token_type `Bearer`, which is returned in a successful token response
BearerToken = "Bearer" BearerToken = "Bearer"
PrefixBearer = BearerToken + " " PrefixBearer = BearerToken + " "
@ -91,62 +91,62 @@ type accessTokenClaims struct {
signatureAlg jose.SignatureAlgorithm `json:"-"` signatureAlg jose.SignatureAlgorithm `json:"-"`
} }
//GetIssuer implements the Claims interface // GetIssuer implements the Claims interface
func (a *accessTokenClaims) GetIssuer() string { func (a *accessTokenClaims) GetIssuer() string {
return a.Issuer return a.Issuer
} }
//GetAudience implements the Claims interface // GetAudience implements the Claims interface
func (a *accessTokenClaims) GetAudience() []string { func (a *accessTokenClaims) GetAudience() []string {
return a.Audience return a.Audience
} }
//GetExpiration implements the Claims interface // GetExpiration implements the Claims interface
func (a *accessTokenClaims) GetExpiration() time.Time { func (a *accessTokenClaims) GetExpiration() time.Time {
return time.Time(a.Expiration) return time.Time(a.Expiration)
} }
//GetIssuedAt implements the Claims interface // GetIssuedAt implements the Claims interface
func (a *accessTokenClaims) GetIssuedAt() time.Time { func (a *accessTokenClaims) GetIssuedAt() time.Time {
return time.Time(a.IssuedAt) return time.Time(a.IssuedAt)
} }
//GetNonce implements the Claims interface // GetNonce implements the Claims interface
func (a *accessTokenClaims) GetNonce() string { func (a *accessTokenClaims) GetNonce() string {
return a.Nonce return a.Nonce
} }
//GetAuthenticationContextClassReference implements the Claims interface // GetAuthenticationContextClassReference implements the Claims interface
func (a *accessTokenClaims) GetAuthenticationContextClassReference() string { func (a *accessTokenClaims) GetAuthenticationContextClassReference() string {
return a.AuthenticationContextClassReference return a.AuthenticationContextClassReference
} }
//GetAuthTime implements the Claims interface // GetAuthTime implements the Claims interface
func (a *accessTokenClaims) GetAuthTime() time.Time { func (a *accessTokenClaims) GetAuthTime() time.Time {
return time.Time(a.AuthTime) return time.Time(a.AuthTime)
} }
//GetAuthorizedParty implements the Claims interface // GetAuthorizedParty implements the Claims interface
func (a *accessTokenClaims) GetAuthorizedParty() string { func (a *accessTokenClaims) GetAuthorizedParty() string {
return a.AuthorizedParty return a.AuthorizedParty
} }
//SetSignatureAlgorithm implements the Claims interface // SetSignatureAlgorithm implements the Claims interface
func (a *accessTokenClaims) SetSignatureAlgorithm(algorithm jose.SignatureAlgorithm) { func (a *accessTokenClaims) SetSignatureAlgorithm(algorithm jose.SignatureAlgorithm) {
a.signatureAlg = algorithm a.signatureAlg = algorithm
} }
//GetSubject implements the AccessTokenClaims interface // GetSubject implements the AccessTokenClaims interface
func (a *accessTokenClaims) GetSubject() string { func (a *accessTokenClaims) GetSubject() string {
return a.Subject return a.Subject
} }
//GetTokenID implements the AccessTokenClaims interface // GetTokenID implements the AccessTokenClaims interface
func (a *accessTokenClaims) GetTokenID() string { func (a *accessTokenClaims) GetTokenID() string {
return a.JWTID return a.JWTID
} }
//SetPrivateClaims implements the AccessTokenClaims interface // SetPrivateClaims implements the AccessTokenClaims interface
func (a *accessTokenClaims) SetPrivateClaims(claims map[string]interface{}) { func (a *accessTokenClaims) SetPrivateClaims(claims map[string]interface{}) {
a.claims = claims a.claims = claims
} }
@ -243,97 +243,97 @@ type idTokenClaims struct {
signatureAlg jose.SignatureAlgorithm signatureAlg jose.SignatureAlgorithm
} }
//GetIssuer implements the Claims interface // GetIssuer implements the Claims interface
func (t *idTokenClaims) GetIssuer() string { func (t *idTokenClaims) GetIssuer() string {
return t.Issuer return t.Issuer
} }
//GetAudience implements the Claims interface // GetAudience implements the Claims interface
func (t *idTokenClaims) GetAudience() []string { func (t *idTokenClaims) GetAudience() []string {
return t.Audience return t.Audience
} }
//GetExpiration implements the Claims interface // GetExpiration implements the Claims interface
func (t *idTokenClaims) GetExpiration() time.Time { func (t *idTokenClaims) GetExpiration() time.Time {
return time.Time(t.Expiration) return time.Time(t.Expiration)
} }
//GetIssuedAt implements the Claims interface // GetIssuedAt implements the Claims interface
func (t *idTokenClaims) GetIssuedAt() time.Time { func (t *idTokenClaims) GetIssuedAt() time.Time {
return time.Time(t.IssuedAt) return time.Time(t.IssuedAt)
} }
//GetNonce implements the Claims interface // GetNonce implements the Claims interface
func (t *idTokenClaims) GetNonce() string { func (t *idTokenClaims) GetNonce() string {
return t.Nonce return t.Nonce
} }
//GetAuthenticationContextClassReference implements the Claims interface // GetAuthenticationContextClassReference implements the Claims interface
func (t *idTokenClaims) GetAuthenticationContextClassReference() string { func (t *idTokenClaims) GetAuthenticationContextClassReference() string {
return t.AuthenticationContextClassReference return t.AuthenticationContextClassReference
} }
//GetAuthTime implements the Claims interface // GetAuthTime implements the Claims interface
func (t *idTokenClaims) GetAuthTime() time.Time { func (t *idTokenClaims) GetAuthTime() time.Time {
return time.Time(t.AuthTime) return time.Time(t.AuthTime)
} }
//GetAuthorizedParty implements the Claims interface // GetAuthorizedParty implements the Claims interface
func (t *idTokenClaims) GetAuthorizedParty() string { func (t *idTokenClaims) GetAuthorizedParty() string {
return t.AuthorizedParty return t.AuthorizedParty
} }
//SetSignatureAlgorithm implements the Claims interface // SetSignatureAlgorithm implements the Claims interface
func (t *idTokenClaims) SetSignatureAlgorithm(alg jose.SignatureAlgorithm) { func (t *idTokenClaims) SetSignatureAlgorithm(alg jose.SignatureAlgorithm) {
t.signatureAlg = alg t.signatureAlg = alg
} }
//GetNotBefore implements the IDTokenClaims interface // GetNotBefore implements the IDTokenClaims interface
func (t *idTokenClaims) GetNotBefore() time.Time { func (t *idTokenClaims) GetNotBefore() time.Time {
return time.Time(t.NotBefore) return time.Time(t.NotBefore)
} }
//GetJWTID implements the IDTokenClaims interface // GetJWTID implements the IDTokenClaims interface
func (t *idTokenClaims) GetJWTID() string { func (t *idTokenClaims) GetJWTID() string {
return t.JWTID return t.JWTID
} }
//GetAccessTokenHash implements the IDTokenClaims interface // GetAccessTokenHash implements the IDTokenClaims interface
func (t *idTokenClaims) GetAccessTokenHash() string { func (t *idTokenClaims) GetAccessTokenHash() string {
return t.AccessTokenHash return t.AccessTokenHash
} }
//GetCodeHash implements the IDTokenClaims interface // GetCodeHash implements the IDTokenClaims interface
func (t *idTokenClaims) GetCodeHash() string { func (t *idTokenClaims) GetCodeHash() string {
return t.CodeHash return t.CodeHash
} }
//GetAuthenticationMethodsReferences implements the IDTokenClaims interface // GetAuthenticationMethodsReferences implements the IDTokenClaims interface
func (t *idTokenClaims) GetAuthenticationMethodsReferences() []string { func (t *idTokenClaims) GetAuthenticationMethodsReferences() []string {
return t.AuthenticationMethodsReferences return t.AuthenticationMethodsReferences
} }
//GetClientID implements the IDTokenClaims interface // GetClientID implements the IDTokenClaims interface
func (t *idTokenClaims) GetClientID() string { func (t *idTokenClaims) GetClientID() string {
return t.ClientID return t.ClientID
} }
//GetSignatureAlgorithm implements the IDTokenClaims interface // GetSignatureAlgorithm implements the IDTokenClaims interface
func (t *idTokenClaims) GetSignatureAlgorithm() jose.SignatureAlgorithm { func (t *idTokenClaims) GetSignatureAlgorithm() jose.SignatureAlgorithm {
return t.signatureAlg return t.signatureAlg
} }
//SetAccessTokenHash implements the IDTokenClaims interface // SetAccessTokenHash implements the IDTokenClaims interface
func (t *idTokenClaims) SetAccessTokenHash(hash string) { func (t *idTokenClaims) SetAccessTokenHash(hash string) {
t.AccessTokenHash = hash t.AccessTokenHash = hash
} }
//SetUserinfo implements the IDTokenClaims interface // SetUserinfo implements the IDTokenClaims interface
func (t *idTokenClaims) SetUserinfo(info UserInfo) { func (t *idTokenClaims) SetUserinfo(info UserInfo) {
t.UserInfo = info t.UserInfo = info
} }
//SetCodeHash implements the IDTokenClaims interface // SetCodeHash implements the IDTokenClaims interface
func (t *idTokenClaims) SetCodeHash(hash string) { func (t *idTokenClaims) SetCodeHash(hash string) {
t.CodeHash = hash t.CodeHash = hash
} }

View file

@ -9,33 +9,34 @@ import (
) )
const ( const (
//GrantTypeCode defines the grant_type `authorization_code` used for the Token Request in the Authorization Code Flow // GrantTypeCode defines the grant_type `authorization_code` used for the Token Request in the Authorization Code Flow
GrantTypeCode GrantType = "authorization_code" GrantTypeCode GrantType = "authorization_code"
//GrantTypeRefreshToken defines the grant_type `refresh_token` used for the Token Request in the Refresh Token Flow // GrantTypeRefreshToken defines the grant_type `refresh_token` used for the Token Request in the Refresh Token Flow
GrantTypeRefreshToken GrantType = "refresh_token" GrantTypeRefreshToken GrantType = "refresh_token"
//GrantTypeClientCredentials defines the grant_type `client_credentials` used for the Token Request in the Client Credentials Token Flow // GrantTypeClientCredentials defines the grant_type `client_credentials` used for the Token Request in the Client Credentials Token Flow
GrantTypeClientCredentials GrantType = "client_credentials" GrantTypeClientCredentials GrantType = "client_credentials"
//GrantTypeBearer defines the grant_type `urn:ietf:params:oauth:grant-type:jwt-bearer` used for the JWT Authorization Grant // GrantTypeBearer defines the grant_type `urn:ietf:params:oauth:grant-type:jwt-bearer` used for the JWT Authorization Grant
GrantTypeBearer GrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" GrantTypeBearer GrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
//GrantTypeTokenExchange defines the grant_type `urn:ietf:params:oauth:grant-type:token-exchange` used for the OAuth Token Exchange Grant // GrantTypeTokenExchange defines the grant_type `urn:ietf:params:oauth:grant-type:token-exchange` used for the OAuth Token Exchange Grant
GrantTypeTokenExchange GrantType = "urn:ietf:params:oauth:grant-type:token-exchange" GrantTypeTokenExchange GrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
//GrantTypeImplicit defines the grant type `implicit` used for implicit flows that skip the generation and exchange of an Authorization Code // GrantTypeImplicit defines the grant type `implicit` used for implicit flows that skip the generation and exchange of an Authorization Code
GrantTypeImplicit GrantType = "implicit" GrantTypeImplicit GrantType = "implicit"
//ClientAssertionTypeJWTAssertion defines the client_assertion_type `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` // ClientAssertionTypeJWTAssertion defines the client_assertion_type `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`
//used for the OAuth JWT Profile Client Authentication // used for the OAuth JWT Profile Client Authentication
ClientAssertionTypeJWTAssertion = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" ClientAssertionTypeJWTAssertion = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
) )
var AllGrantTypes = []GrantType{ var AllGrantTypes = []GrantType{
GrantTypeCode, GrantTypeRefreshToken, GrantTypeClientCredentials, GrantTypeCode, GrantTypeRefreshToken, GrantTypeClientCredentials,
GrantTypeBearer, GrantTypeTokenExchange, GrantTypeImplicit, GrantTypeBearer, GrantTypeTokenExchange, GrantTypeImplicit,
ClientAssertionTypeJWTAssertion} ClientAssertionTypeJWTAssertion,
}
type GrantType string type GrantType string
@ -60,12 +61,12 @@ func (a *AccessTokenRequest) GrantType() GrantType {
return GrantTypeCode return GrantTypeCode
} }
//SetClientID implements op.AuthenticatedTokenRequest // SetClientID implements op.AuthenticatedTokenRequest
func (a *AccessTokenRequest) SetClientID(clientID string) { func (a *AccessTokenRequest) SetClientID(clientID string) {
a.ClientID = clientID a.ClientID = clientID
} }
//SetClientSecret implements op.AuthenticatedTokenRequest // SetClientSecret implements op.AuthenticatedTokenRequest
func (a *AccessTokenRequest) SetClientSecret(clientSecret string) { func (a *AccessTokenRequest) SetClientSecret(clientSecret string) {
a.ClientSecret = clientSecret a.ClientSecret = clientSecret
} }
@ -85,12 +86,12 @@ func (a *RefreshTokenRequest) GrantType() GrantType {
return GrantTypeRefreshToken return GrantTypeRefreshToken
} }
//SetClientID implements op.AuthenticatedTokenRequest // SetClientID implements op.AuthenticatedTokenRequest
func (a *RefreshTokenRequest) SetClientID(clientID string) { func (a *RefreshTokenRequest) SetClientID(clientID string) {
a.ClientID = clientID a.ClientID = clientID
} }
//SetClientSecret implements op.AuthenticatedTokenRequest // SetClientSecret implements op.AuthenticatedTokenRequest
func (a *RefreshTokenRequest) SetClientSecret(clientSecret string) { func (a *RefreshTokenRequest) SetClientSecret(clientSecret string) {
a.ClientSecret = clientSecret a.ClientSecret = clientSecret
} }
@ -148,55 +149,55 @@ func (j *JWTTokenRequest) GetCustomClaim(key string) interface{} {
return j.private[key] return j.private[key]
} }
//GetIssuer implements the Claims interface // GetIssuer implements the Claims interface
func (j *JWTTokenRequest) GetIssuer() string { func (j *JWTTokenRequest) GetIssuer() string {
return j.Issuer return j.Issuer
} }
//GetAudience implements the Claims and TokenRequest interfaces // GetAudience implements the Claims and TokenRequest interfaces
func (j *JWTTokenRequest) GetAudience() []string { func (j *JWTTokenRequest) GetAudience() []string {
return j.Audience return j.Audience
} }
//GetExpiration implements the Claims interface // GetExpiration implements the Claims interface
func (j *JWTTokenRequest) GetExpiration() time.Time { func (j *JWTTokenRequest) GetExpiration() time.Time {
return time.Time(j.ExpiresAt) return time.Time(j.ExpiresAt)
} }
//GetIssuedAt implements the Claims interface // GetIssuedAt implements the Claims interface
func (j *JWTTokenRequest) GetIssuedAt() time.Time { func (j *JWTTokenRequest) GetIssuedAt() time.Time {
return time.Time(j.IssuedAt) return time.Time(j.IssuedAt)
} }
//GetNonce implements the Claims interface // GetNonce implements the Claims interface
func (j *JWTTokenRequest) GetNonce() string { func (j *JWTTokenRequest) GetNonce() string {
return "" return ""
} }
//GetAuthenticationContextClassReference implements the Claims interface // GetAuthenticationContextClassReference implements the Claims interface
func (j *JWTTokenRequest) GetAuthenticationContextClassReference() string { func (j *JWTTokenRequest) GetAuthenticationContextClassReference() string {
return "" return ""
} }
//GetAuthTime implements the Claims interface // GetAuthTime implements the Claims interface
func (j *JWTTokenRequest) GetAuthTime() time.Time { func (j *JWTTokenRequest) GetAuthTime() time.Time {
return time.Time{} return time.Time{}
} }
//GetAuthorizedParty implements the Claims interface // GetAuthorizedParty implements the Claims interface
func (j *JWTTokenRequest) GetAuthorizedParty() string { func (j *JWTTokenRequest) GetAuthorizedParty() string {
return "" return ""
} }
//SetSignatureAlgorithm implements the Claims interface // SetSignatureAlgorithm implements the Claims interface
func (j *JWTTokenRequest) SetSignatureAlgorithm(_ jose.SignatureAlgorithm) {} func (j *JWTTokenRequest) SetSignatureAlgorithm(_ jose.SignatureAlgorithm) {}
//GetSubject implements the TokenRequest interface // GetSubject implements the TokenRequest interface
func (j *JWTTokenRequest) GetSubject() string { func (j *JWTTokenRequest) GetSubject() string {
return j.Subject return j.Subject
} }
//GetScopes implements the TokenRequest interface // GetScopes implements the TokenRequest interface
func (j *JWTTokenRequest) GetScopes() []string { func (j *JWTTokenRequest) GetScopes() []string {
return j.Scopes return j.Scopes
} }

View file

@ -61,11 +61,11 @@ type Verifier interface {
Offset() time.Duration Offset() time.Duration
} }
//ACRVerifier specifies the function to be used by the `DefaultVerifier` for validating the acr claim // ACRVerifier specifies the function to be used by the `DefaultVerifier` for validating the acr claim
type ACRVerifier func(string) error type ACRVerifier func(string) error
//DefaultACRVerifier implements `ACRVerifier` returning an error // DefaultACRVerifier implements `ACRVerifier` returning an error
//if none of the provided values matches the acr claim // if none of the provided values matches the acr claim
func DefaultACRVerifier(possibleValues []string) ACRVerifier { func DefaultACRVerifier(possibleValues []string) ACRVerifier {
return func(acr string) error { return func(acr string) error {
if !str.Contains(possibleValues, acr) { if !str.Contains(possibleValues, acr) {
@ -76,7 +76,7 @@ func DefaultACRVerifier(possibleValues []string) ACRVerifier {
} }
func DecryptToken(tokenString string) (string, error) { func DecryptToken(tokenString string) (string, error) {
return tokenString, nil //TODO: impl return tokenString, nil // TODO: impl
} }
func ParseToken(tokenString string, claims interface{}) ([]byte, error) { func ParseToken(tokenString string, claims interface{}) ([]byte, error) {
@ -111,7 +111,7 @@ func CheckAudience(claims Claims, clientID string) error {
return fmt.Errorf("%w: Audience must contain client_id %q", ErrAudience, clientID) return fmt.Errorf("%w: Audience must contain client_id %q", ErrAudience, clientID)
} }
//TODO: check aud trusted // TODO: check aud trusted
return nil return nil
} }
@ -202,6 +202,7 @@ func CheckAuthorizationContextClassReference(claims Claims, acr ACRVerifier) err
} }
return nil return nil
} }
func CheckAuthTime(claims Claims, maxAge time.Duration) error { func CheckAuthTime(claims Claims, maxAge time.Duration) error {
if maxAge == 0 { if maxAge == 0 {
return nil return nil

View file

@ -45,8 +45,8 @@ type Authorizer interface {
RequestObjectSupported() bool RequestObjectSupported() bool
} }
//AuthorizeValidator is an extension of Authorizer interface // AuthorizeValidator is an extension of Authorizer interface
//implementing its own validation mechanism for the auth request // implementing its own validation mechanism for the auth request
type AuthorizeValidator interface { type AuthorizeValidator interface {
Authorizer Authorizer
ValidateAuthRequest(context.Context, *oidc.AuthRequest, Storage, IDTokenHintVerifier) (string, error) ValidateAuthRequest(context.Context, *oidc.AuthRequest, Storage, IDTokenHintVerifier) (string, error)
@ -64,8 +64,8 @@ func authorizeCallbackHandler(authorizer Authorizer) func(http.ResponseWriter, *
} }
} }
//Authorize handles the authorization request, including // Authorize handles the authorization request, including
//parsing, validating, storing and finally redirecting to the login handler // parsing, validating, storing and finally redirecting to the login handler
func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) {
authReq, err := ParseAuthorizeRequest(r, authorizer.Decoder()) authReq, err := ParseAuthorizeRequest(r, authorizer.Decoder())
if err != nil { if err != nil {
@ -113,7 +113,7 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) {
RedirectToLogin(req.GetID(), client, w, r) RedirectToLogin(req.GetID(), client, w, r)
} }
//ParseAuthorizeRequest parsed the http request into an oidc.AuthRequest // ParseAuthorizeRequest parsed the http request into an oidc.AuthRequest
func ParseAuthorizeRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.AuthRequest, error) { func ParseAuthorizeRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.AuthRequest, error) {
err := r.ParseForm() err := r.ParseForm()
if err != nil { if err != nil {
@ -127,8 +127,8 @@ func ParseAuthorizeRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.A
return authReq, nil return authReq, nil
} }
//ParseRequestObject parse the `request` parameter, validates the token including the signature // ParseRequestObject parse the `request` parameter, validates the token including the signature
//and copies the token claims into the auth request // and copies the token claims into the auth request
func ParseRequestObject(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, issuer string) (*oidc.AuthRequest, error) { func ParseRequestObject(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, issuer string) (*oidc.AuthRequest, error) {
requestObject := new(oidc.RequestObject) requestObject := new(oidc.RequestObject)
payload, err := oidc.ParseToken(authReq.RequestParam, requestObject) payload, err := oidc.ParseToken(authReq.RequestParam, requestObject)
@ -156,8 +156,8 @@ func ParseRequestObject(ctx context.Context, authReq *oidc.AuthRequest, storage
return authReq, nil return authReq, nil
} }
//CopyRequestObjectToAuthRequest overwrites present values from the Request Object into the auth request // CopyRequestObjectToAuthRequest overwrites present values from the Request Object into the auth request
//and clears the `RequestParam` of the auth request // and clears the `RequestParam` of the auth request
func CopyRequestObjectToAuthRequest(authReq *oidc.AuthRequest, requestObject *oidc.RequestObject) { func CopyRequestObjectToAuthRequest(authReq *oidc.AuthRequest, requestObject *oidc.RequestObject) {
if str.Contains(authReq.Scopes, oidc.ScopeOpenID) && len(requestObject.Scopes) > 0 { if str.Contains(authReq.Scopes, oidc.ScopeOpenID) && len(requestObject.Scopes) > 0 {
authReq.Scopes = requestObject.Scopes authReq.Scopes = requestObject.Scopes
@ -204,7 +204,7 @@ func CopyRequestObjectToAuthRequest(authReq *oidc.AuthRequest, requestObject *oi
authReq.RequestParam = "" authReq.RequestParam = ""
} }
//ValidateAuthRequest validates the authorize parameters and returns the userID of the id_token_hint if passed // ValidateAuthRequest validates the authorize parameters and returns the userID of the id_token_hint if passed
func ValidateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, verifier IDTokenHintVerifier) (sub string, err error) { func ValidateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, verifier IDTokenHintVerifier) (sub string, err error) {
authReq.MaxAge, err = ValidateAuthReqPrompt(authReq.Prompt, authReq.MaxAge) authReq.MaxAge, err = ValidateAuthReqPrompt(authReq.Prompt, authReq.MaxAge)
if err != nil { if err != nil {
@ -227,7 +227,7 @@ func ValidateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, storage
return ValidateAuthReqIDTokenHint(ctx, authReq.IDTokenHint, verifier) return ValidateAuthReqIDTokenHint(ctx, authReq.IDTokenHint, verifier)
} }
//ValidateAuthReqPrompt validates the passed prompt values and sets max_age to 0 if prompt login is present // ValidateAuthReqPrompt validates the passed prompt values and sets max_age to 0 if prompt login is present
func ValidateAuthReqPrompt(prompts []string, maxAge *uint) (_ *uint, err error) { func ValidateAuthReqPrompt(prompts []string, maxAge *uint) (_ *uint, err error) {
for _, prompt := range prompts { for _, prompt := range prompts {
if prompt == oidc.PromptNone && len(prompts) > 1 { if prompt == oidc.PromptNone && len(prompts) > 1 {
@ -240,7 +240,7 @@ func ValidateAuthReqPrompt(prompts []string, maxAge *uint) (_ *uint, err error)
return maxAge, nil return maxAge, nil
} }
//ValidateAuthReqScopes validates the passed scopes // ValidateAuthReqScopes validates the passed scopes
func ValidateAuthReqScopes(client Client, scopes []string) ([]string, error) { func ValidateAuthReqScopes(client Client, scopes []string) ([]string, error) {
if len(scopes) == 0 { if len(scopes) == 0 {
return nil, oidc.ErrInvalidRequest(). return nil, oidc.ErrInvalidRequest().
@ -274,7 +274,7 @@ func ValidateAuthReqScopes(client Client, scopes []string) ([]string, error) {
return scopes, nil return scopes, nil
} }
//ValidateAuthReqRedirectURI validates the passed redirect_uri and response_type to the registered uris and client type // ValidateAuthReqRedirectURI validates the passed redirect_uri and response_type to the registered uris and client type
func ValidateAuthReqRedirectURI(client Client, uri string, responseType oidc.ResponseType) error { func ValidateAuthReqRedirectURI(client Client, uri string, responseType oidc.ResponseType) error {
if uri == "" { if uri == "" {
return oidc.ErrInvalidRequestRedirectURI().WithDescription("The redirect_uri is missing in the request. " + return oidc.ErrInvalidRequestRedirectURI().WithDescription("The redirect_uri is missing in the request. " +
@ -309,7 +309,7 @@ func ValidateAuthReqRedirectURI(client Client, uri string, responseType oidc.Res
"If you have any questions, you may contact the administrator of the application.") "If you have any questions, you may contact the administrator of the application.")
} }
//ValidateAuthReqRedirectURINative validates the passed redirect_uri and response_type to the registered uris and client type // ValidateAuthReqRedirectURINative validates the passed redirect_uri and response_type to the registered uris and client type
func validateAuthReqRedirectURINative(client Client, uri string, responseType oidc.ResponseType) error { func validateAuthReqRedirectURINative(client Client, uri string, responseType oidc.ResponseType) error {
parsedURL, isLoopback := HTTPLoopbackOrLocalhost(uri) parsedURL, isLoopback := HTTPLoopbackOrLocalhost(uri)
isCustomSchema := !strings.HasPrefix(uri, "http://") isCustomSchema := !strings.HasPrefix(uri, "http://")
@ -350,7 +350,7 @@ func HTTPLoopbackOrLocalhost(rawurl string) (*url.URL, bool) {
return parsedURL, hostName == "localhost" || net.ParseIP(hostName).IsLoopback() return parsedURL, hostName == "localhost" || net.ParseIP(hostName).IsLoopback()
} }
//ValidateAuthReqResponseType validates the passed response_type to the registered response types // ValidateAuthReqResponseType validates the passed response_type to the registered response types
func ValidateAuthReqResponseType(client Client, responseType oidc.ResponseType) error { func ValidateAuthReqResponseType(client Client, responseType oidc.ResponseType) error {
if responseType == "" { if responseType == "" {
return oidc.ErrInvalidRequest().WithDescription("The response type is missing in your request. " + return oidc.ErrInvalidRequest().WithDescription("The response type is missing in your request. " +
@ -363,8 +363,8 @@ func ValidateAuthReqResponseType(client Client, responseType oidc.ResponseType)
return nil return nil
} }
//ValidateAuthReqIDTokenHint validates the id_token_hint (if passed as parameter in the request) // ValidateAuthReqIDTokenHint validates the id_token_hint (if passed as parameter in the request)
//and returns the `sub` claim // and returns the `sub` claim
func ValidateAuthReqIDTokenHint(ctx context.Context, idTokenHint string, verifier IDTokenHintVerifier) (string, error) { func ValidateAuthReqIDTokenHint(ctx context.Context, idTokenHint string, verifier IDTokenHintVerifier) (string, error) {
if idTokenHint == "" { if idTokenHint == "" {
return "", nil return "", nil
@ -377,13 +377,13 @@ func ValidateAuthReqIDTokenHint(ctx context.Context, idTokenHint string, verifie
return claims.GetSubject(), nil return claims.GetSubject(), nil
} }
//RedirectToLogin redirects the end user to the Login UI for authentication // RedirectToLogin redirects the end user to the Login UI for authentication
func RedirectToLogin(authReqID string, client Client, w http.ResponseWriter, r *http.Request) { func RedirectToLogin(authReqID string, client Client, w http.ResponseWriter, r *http.Request) {
login := client.LoginURL(authReqID) login := client.LoginURL(authReqID)
http.Redirect(w, r, login, http.StatusFound) http.Redirect(w, r, login, http.StatusFound)
} }
//AuthorizeCallback handles the callback after authentication in the Login UI // AuthorizeCallback handles the callback after authentication in the Login UI
func AuthorizeCallback(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { func AuthorizeCallback(w http.ResponseWriter, r *http.Request, authorizer Authorizer) {
params := mux.Vars(r) params := mux.Vars(r)
id := params["id"] id := params["id"]
@ -406,7 +406,7 @@ func AuthorizeCallback(w http.ResponseWriter, r *http.Request, authorizer Author
AuthResponse(authReq, authorizer, w, r) AuthResponse(authReq, authorizer, w, r)
} }
//AuthResponse creates the successful authentication response (either code or tokens) // AuthResponse creates the successful authentication response (either code or tokens)
func AuthResponse(authReq AuthRequest, authorizer Authorizer, w http.ResponseWriter, r *http.Request) { func AuthResponse(authReq AuthRequest, authorizer Authorizer, w http.ResponseWriter, r *http.Request) {
client, err := authorizer.Storage().GetClientByClientID(r.Context(), authReq.GetClientID()) client, err := authorizer.Storage().GetClientByClientID(r.Context(), authReq.GetClientID())
if err != nil { if err != nil {
@ -420,7 +420,7 @@ func AuthResponse(authReq AuthRequest, authorizer Authorizer, w http.ResponseWri
AuthResponseToken(w, r, authReq, authorizer, client) AuthResponseToken(w, r, authReq, authorizer, client)
} }
//AuthResponseCode creates the successful code authentication response // AuthResponseCode creates the successful code authentication response
func AuthResponseCode(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer) { func AuthResponseCode(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer) {
code, err := CreateAuthRequestCode(r.Context(), authReq, authorizer.Storage(), authorizer.Crypto()) code, err := CreateAuthRequestCode(r.Context(), authReq, authorizer.Storage(), authorizer.Crypto())
if err != nil { if err != nil {
@ -442,7 +442,7 @@ func AuthResponseCode(w http.ResponseWriter, r *http.Request, authReq AuthReques
http.Redirect(w, r, callback, http.StatusFound) http.Redirect(w, r, callback, http.StatusFound)
} }
//AuthResponseToken creates the successful token(s) authentication response // AuthResponseToken creates the successful token(s) authentication response
func AuthResponseToken(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer, client Client) { func AuthResponseToken(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer, client Client) {
createAccessToken := authReq.GetResponseType() != oidc.ResponseTypeIDTokenOnly createAccessToken := authReq.GetResponseType() != oidc.ResponseTypeIDTokenOnly
resp, err := CreateTokenResponse(r.Context(), authReq, client, authorizer, createAccessToken, "", "") resp, err := CreateTokenResponse(r.Context(), authReq, client, authorizer, createAccessToken, "", "")
@ -458,7 +458,7 @@ func AuthResponseToken(w http.ResponseWriter, r *http.Request, authReq AuthReque
http.Redirect(w, r, callback, http.StatusFound) http.Redirect(w, r, callback, http.StatusFound)
} }
//CreateAuthRequestCode creates and stores a code for the auth code response // CreateAuthRequestCode creates and stores a code for the auth code response
func CreateAuthRequestCode(ctx context.Context, authReq AuthRequest, storage Storage, crypto Crypto) (string, error) { func CreateAuthRequestCode(ctx context.Context, authReq AuthRequest, storage Storage, crypto Crypto) (string, error) {
code, err := BuildAuthRequestCode(authReq, crypto) code, err := BuildAuthRequestCode(authReq, crypto)
if err != nil { if err != nil {
@ -470,13 +470,13 @@ func CreateAuthRequestCode(ctx context.Context, authReq AuthRequest, storage Sto
return code, nil return code, nil
} }
//BuildAuthRequestCode builds the string representation of the auth code // BuildAuthRequestCode builds the string representation of the auth code
func BuildAuthRequestCode(authReq AuthRequest, crypto Crypto) (string, error) { func BuildAuthRequestCode(authReq AuthRequest, crypto Crypto) (string, error) {
return crypto.Encrypt(authReq.GetID()) return crypto.Encrypt(authReq.GetID())
} }
//AuthResponseURL encodes the authorization response (successful and error) and sets it as query or fragment values // AuthResponseURL encodes the authorization response (successful and error) and sets it as query or fragment values
//depending on the response_mode and response_type // depending on the response_mode and response_type
func AuthResponseURL(redirectURI string, responseType oidc.ResponseType, responseMode oidc.ResponseMode, response interface{}, encoder httphelper.Encoder) (string, error) { func AuthResponseURL(redirectURI string, responseType oidc.ResponseType, responseMode oidc.ResponseMode, response interface{}, encoder httphelper.Encoder) (string, error) {
uri, err := url.Parse(redirectURI) uri, err := url.Parse(redirectURI)
if err != nil { if err != nil {
@ -486,18 +486,18 @@ func AuthResponseURL(redirectURI string, responseType oidc.ResponseType, respons
if err != nil { if err != nil {
return "", oidc.ErrServerError().WithParent(err) return "", oidc.ErrServerError().WithParent(err)
} }
//return explicitly requested mode // return explicitly requested mode
if responseMode == oidc.ResponseModeQuery { if responseMode == oidc.ResponseModeQuery {
return mergeQueryParams(uri, params), nil return mergeQueryParams(uri, params), nil
} }
if responseMode == oidc.ResponseModeFragment { if responseMode == oidc.ResponseModeFragment {
return setFragment(uri, params), nil return setFragment(uri, params), nil
} }
//implicit must use fragment mode is not specified by client // implicit must use fragment mode is not specified by client
if responseType == oidc.ResponseTypeIDToken || responseType == oidc.ResponseTypeIDTokenOnly { if responseType == oidc.ResponseTypeIDToken || responseType == oidc.ResponseTypeIDTokenOnly {
return setFragment(uri, params), nil return setFragment(uri, params), nil
} }
//if we get here it's code flow: defaults to query // if we get here it's code flow: defaults to query
return mergeQueryParams(uri, params), nil return mergeQueryParams(uri, params), nil
} }

View file

@ -20,8 +20,8 @@ import (
) )
// //
//TOOD: tests will be implemented in branch for service accounts // TOOD: tests will be implemented in branch for service accounts
//func TestAuthorize(t *testing.T) { // func TestAuthorize(t *testing.T) {
// // testCallback := func(t *testing.T, clienID string) callbackHandler { // // testCallback := func(t *testing.T, clienID string) callbackHandler {
// // return func(authReq *oidc.AuthRequest, client oidc.Client, w http.ResponseWriter, r *http.Request) { // // return func(authReq *oidc.AuthRequest, client oidc.Client, w http.ResponseWriter, r *http.Request) {
// // // require.Equal(t, clientID, client.) // // // require.Equal(t, clientID, client.)
@ -364,191 +364,245 @@ func TestValidateAuthReqRedirectURI(t *testing.T) {
}{ }{
{ {
"empty fails", "empty fails",
args{"", args{
"",
mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeWeb, nil, false), mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeWeb, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"unregistered https fails", "unregistered https fails",
args{"https://unregistered.com/callback", args{
"https://unregistered.com/callback",
mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeWeb, nil, false), mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeWeb, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"unregistered http fails", "unregistered http fails",
args{"http://unregistered.com/callback", args{
"http://unregistered.com/callback",
mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeWeb, nil, false), mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeWeb, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"code flow registered https web ok", "code flow registered https web ok",
args{"https://registered.com/callback", args{
"https://registered.com/callback",
mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeWeb, nil, false), mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeWeb, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"code flow registered https native ok", "code flow registered https native ok",
args{"https://registered.com/callback", args{
"https://registered.com/callback",
mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"code flow registered https user agent ok", "code flow registered https user agent ok",
args{"https://registered.com/callback", args{
"https://registered.com/callback",
mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, false), mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"code flow registered http confidential (web) ok", "code flow registered http confidential (web) ok",
args{"http://registered.com/callback", args{
"http://registered.com/callback",
mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeWeb, nil, false), mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeWeb, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"code flow registered http not confidential (native) fails", "code flow registered http not confidential (native) fails",
args{"http://registered.com/callback", args{
"http://registered.com/callback",
mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"code flow registered http not confidential (user agent) fails", "code flow registered http not confidential (user agent) fails",
args{"http://registered.com/callback", args{
"http://registered.com/callback",
mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, false), mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"code flow registered http localhost native ok", "code flow registered http localhost native ok",
args{"http://localhost:4200/callback", args{
"http://localhost:4200/callback",
mock.NewClientWithConfig(t, []string{"http://localhost/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"http://localhost/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"code flow registered http loopback v4 native ok", "code flow registered http loopback v4 native ok",
args{"http://127.0.0.1:4200/callback", args{
"http://127.0.0.1:4200/callback",
mock.NewClientWithConfig(t, []string{"http://127.0.0.1/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"http://127.0.0.1/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"code flow registered http loopback v6 native ok", "code flow registered http loopback v6 native ok",
args{"http://[::1]:4200/callback", args{
"http://[::1]:4200/callback",
mock.NewClientWithConfig(t, []string{"http://[::1]/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"http://[::1]/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"code flow unregistered http native fails", "code flow unregistered http native fails",
args{"http://unregistered.com/callback", args{
"http://unregistered.com/callback",
mock.NewClientWithConfig(t, []string{"http://locahost/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"http://locahost/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"code flow unregistered custom native fails", "code flow unregistered custom native fails",
args{"unregistered://callback", args{
"unregistered://callback",
mock.NewClientWithConfig(t, []string{"registered://callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"registered://callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"code flow unregistered loopback native fails", "code flow unregistered loopback native fails",
args{"http://[::1]:4200/unregistered", args{
"http://[::1]:4200/unregistered",
mock.NewClientWithConfig(t, []string{"http://[::1]:4200/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"http://[::1]:4200/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"code flow registered custom not native (web) fails", "code flow registered custom not native (web) fails",
args{"custom://callback", args{
"custom://callback",
mock.NewClientWithConfig(t, []string{"custom://callback"}, op.ApplicationTypeWeb, nil, false), mock.NewClientWithConfig(t, []string{"custom://callback"}, op.ApplicationTypeWeb, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"code flow registered custom not native (user agent) fails", "code flow registered custom not native (user agent) fails",
args{"custom://callback", args{
"custom://callback",
mock.NewClientWithConfig(t, []string{"custom://callback"}, op.ApplicationTypeUserAgent, nil, false), mock.NewClientWithConfig(t, []string{"custom://callback"}, op.ApplicationTypeUserAgent, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
true, true,
}, },
{ {
"code flow registered custom native ok", "code flow registered custom native ok",
args{"custom://callback", args{
"custom://callback",
mock.NewClientWithConfig(t, []string{"custom://callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"custom://callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"code flow dev mode http ok", "code flow dev mode http ok",
args{"http://registered.com/callback", args{
"http://registered.com/callback",
mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, true), mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, true),
oidc.ResponseTypeCode}, oidc.ResponseTypeCode,
},
false, false,
}, },
{ {
"implicit flow registered ok", "implicit flow registered ok",
args{"https://registered.com/callback", args{
"https://registered.com/callback",
mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, false), mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, false),
oidc.ResponseTypeIDToken}, oidc.ResponseTypeIDToken,
},
false, false,
}, },
{ {
"implicit flow unregistered fails", "implicit flow unregistered fails",
args{"https://unregistered.com/callback", args{
"https://unregistered.com/callback",
mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, false), mock.NewClientWithConfig(t, []string{"https://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, false),
oidc.ResponseTypeIDToken}, oidc.ResponseTypeIDToken,
},
true, true,
}, },
{ {
"implicit flow registered http localhost native ok", "implicit flow registered http localhost native ok",
args{"http://localhost:9999/callback", args{
"http://localhost:9999/callback",
mock.NewClientWithConfig(t, []string{"http://localhost:9999/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"http://localhost:9999/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeIDToken}, oidc.ResponseTypeIDToken,
},
false, false,
}, },
{ {
"implicit flow registered http localhost web fails", "implicit flow registered http localhost web fails",
args{"http://localhost:9999/callback", args{
"http://localhost:9999/callback",
mock.NewClientWithConfig(t, []string{"http://localhost:9999/callback"}, op.ApplicationTypeWeb, nil, false), mock.NewClientWithConfig(t, []string{"http://localhost:9999/callback"}, op.ApplicationTypeWeb, nil, false),
oidc.ResponseTypeIDToken}, oidc.ResponseTypeIDToken,
},
true, true,
}, },
{ {
"implicit flow registered http localhost user agent fails", "implicit flow registered http localhost user agent fails",
args{"http://localhost:9999/callback", args{
"http://localhost:9999/callback",
mock.NewClientWithConfig(t, []string{"http://localhost:9999/callback"}, op.ApplicationTypeUserAgent, nil, false), mock.NewClientWithConfig(t, []string{"http://localhost:9999/callback"}, op.ApplicationTypeUserAgent, nil, false),
oidc.ResponseTypeIDToken}, oidc.ResponseTypeIDToken,
},
true, true,
}, },
{ {
"implicit flow http non localhost fails", "implicit flow http non localhost fails",
args{"http://registered.com/callback", args{
"http://registered.com/callback",
mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeIDToken}, oidc.ResponseTypeIDToken,
},
true, true,
}, },
{ {
"implicit flow custom fails", "implicit flow custom fails",
args{"custom://callback", args{
"custom://callback",
mock.NewClientWithConfig(t, []string{"custom://callback"}, op.ApplicationTypeNative, nil, false), mock.NewClientWithConfig(t, []string{"custom://callback"}, op.ApplicationTypeNative, nil, false),
oidc.ResponseTypeIDToken}, oidc.ResponseTypeIDToken,
},
false, false,
}, },
{ {
"implicit flow dev mode http ok", "implicit flow dev mode http ok",
args{"http://registered.com/callback", args{
"http://registered.com/callback",
mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, true), mock.NewClientWithConfig(t, []string{"http://registered.com/callback"}, op.ApplicationTypeUserAgent, nil, true),
oidc.ResponseTypeIDToken}, oidc.ResponseTypeIDToken,
},
false, false,
}, },
} }
@ -647,20 +701,26 @@ func TestValidateAuthReqResponseType(t *testing.T) {
}{ }{
{ {
"empty response type", "empty response type",
args{"", args{
mock.NewClientWithConfig(t, nil, op.ApplicationTypeNative, []oidc.ResponseType{oidc.ResponseTypeCode}, true)}, "",
mock.NewClientWithConfig(t, nil, op.ApplicationTypeNative, []oidc.ResponseType{oidc.ResponseTypeCode}, true),
},
true, true,
}, },
{ {
"response type missing in client config", "response type missing in client config",
args{oidc.ResponseTypeIDToken, args{
mock.NewClientWithConfig(t, nil, op.ApplicationTypeNative, []oidc.ResponseType{oidc.ResponseTypeCode}, true)}, oidc.ResponseTypeIDToken,
mock.NewClientWithConfig(t, nil, op.ApplicationTypeNative, []oidc.ResponseType{oidc.ResponseTypeCode}, true),
},
true, true,
}, },
{ {
"valid response type", "valid response type",
args{oidc.ResponseTypeCode, args{
mock.NewClientWithConfig(t, nil, op.ApplicationTypeNative, []oidc.ResponseType{oidc.ResponseTypeCode}, true)}, oidc.ResponseTypeCode,
mock.NewClientWithConfig(t, nil, op.ApplicationTypeNative, []oidc.ResponseType{oidc.ResponseTypeCode}, true),
},
false, false,
}, },
} }

View file

@ -60,7 +60,7 @@ func TestValidateIssuer(t *testing.T) {
true, true,
}, },
} }
//ensure env is not set // ensure env is not set
//nolint:errcheck //nolint:errcheck
os.Unsetenv(OidcDevMode) os.Unsetenv(OidcDevMode)
for _, tt := range tests { for _, tt := range tests {

View file

@ -56,7 +56,7 @@ var DefaultSupportedScopes = []string{
} }
func Scopes(c Configuration) []string { func Scopes(c Configuration) []string {
return DefaultSupportedScopes //TODO: config return DefaultSupportedScopes // TODO: config
} }
func ResponseTypes(c Configuration) []string { func ResponseTypes(c Configuration) []string {
@ -64,7 +64,7 @@ func ResponseTypes(c Configuration) []string {
string(oidc.ResponseTypeCode), string(oidc.ResponseTypeCode),
string(oidc.ResponseTypeIDTokenOnly), string(oidc.ResponseTypeIDTokenOnly),
string(oidc.ResponseTypeIDToken), string(oidc.ResponseTypeIDToken),
} //TODO: ok for now, check later if dynamic needed } // TODO: ok for now, check later if dynamic needed
} }
func GrantTypes(c Configuration) []oidc.GrantType { func GrantTypes(c Configuration) []oidc.GrantType {
@ -88,7 +88,7 @@ func GrantTypes(c Configuration) []oidc.GrantType {
} }
func SupportedClaims(c Configuration) []string { func SupportedClaims(c Configuration) []string {
return []string{ //TODO: config return []string{ // TODO: config
"sub", "sub",
"aud", "aud",
"exp", "exp",
@ -121,7 +121,7 @@ func SigAlgorithms(s Signer) []string {
} }
func SubjectTypes(c Configuration) []string { func SubjectTypes(c Configuration) []string {
return []string{"public"} //TODO: config return []string{"public"} // TODO: config
} }
func AuthMethodsTokenEndpoint(c Configuration) []oidc.AuthMethod { func AuthMethodsTokenEndpoint(c Configuration) []oidc.AuthMethod {

View file

@ -27,7 +27,7 @@ func (e Endpoint) Absolute(host string) string {
} }
func (e Endpoint) Validate() error { func (e Endpoint) Validate() error {
return nil //TODO: return nil // TODO:
} }
func absoluteEndpoint(host, endpoint string) string { func absoluteEndpoint(host, endpoint string) string {

View file

@ -87,7 +87,7 @@ func TestEndpoint_Absolute(t *testing.T) {
} }
} }
//TODO: impl test // TODO: impl test
func TestEndpoint_Validate(t *testing.T) { func TestEndpoint_Validate(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

View file

@ -68,6 +68,7 @@ type Verifier struct{}
func (v *Verifier) Verify(ctx context.Context, accessToken, idToken string) (*oidc.IDTokenClaims, error) { func (v *Verifier) Verify(ctx context.Context, accessToken, idToken string) (*oidc.IDTokenClaims, error) {
return nil, nil return nil, nil
} }
func (v *Verifier) VerifyIDToken(ctx context.Context, idToken string) (*oidc.IDTokenClaims, error) { func (v *Verifier) VerifyIDToken(ctx context.Context, idToken string) (*oidc.IDTokenClaims, error) {
return nil, nil return nil, nil
} }

View file

@ -20,7 +20,8 @@ func NewClientExpectAny(t *testing.T, appType op.ApplicationType) op.Client {
"https://registered.com/callback", "https://registered.com/callback",
"http://registered.com/callback", "http://registered.com/callback",
"http://localhost:9999/callback", "http://localhost:9999/callback",
"custom://callback"}) "custom://callback",
})
m.EXPECT().ApplicationType().AnyTimes().Return(appType) m.EXPECT().ApplicationType().AnyTimes().Return(appType)
m.EXPECT().LoginURL(gomock.Any()).AnyTimes().DoAndReturn( m.EXPECT().LoginURL(gomock.Any()).AnyTimes().DoAndReturn(
func(id string) string { func(id string) string {

View file

@ -44,6 +44,7 @@ func NewMockStorageSigningKeyInvalid(t *testing.T) op.Storage {
ExpectSigningKeyInvalid(m) ExpectSigningKeyInvalid(m)
return m return m
} }
func NewMockStorageSigningKey(t *testing.T) op.Storage { func NewMockStorageSigningKey(t *testing.T) op.Storage {
m := NewStorage(t) m := NewStorage(t)
ExpectSigningKey(m) ExpectSigningKey(m)
@ -120,6 +121,7 @@ func (c *ConfClient) RedirectURIs() []string {
"custom://callback", "custom://callback",
} }
} }
func (c *ConfClient) PostLogoutRedirectURIs() []string { func (c *ConfClient) PostLogoutRedirectURIs() []string {
return []string{} return []string{}
} }
@ -143,34 +145,43 @@ func (c *ConfClient) GetID() string {
func (c *ConfClient) AccessTokenLifetime() time.Duration { func (c *ConfClient) AccessTokenLifetime() time.Duration {
return 5 * time.Minute return 5 * time.Minute
} }
func (c *ConfClient) IDTokenLifetime() time.Duration { func (c *ConfClient) IDTokenLifetime() time.Duration {
return 5 * time.Minute return 5 * time.Minute
} }
func (c *ConfClient) AccessTokenType() op.AccessTokenType { func (c *ConfClient) AccessTokenType() op.AccessTokenType {
return c.accessTokenType return c.accessTokenType
} }
func (c *ConfClient) ResponseTypes() []oidc.ResponseType { func (c *ConfClient) ResponseTypes() []oidc.ResponseType {
return c.responseTypes return c.responseTypes
} }
func (c *ConfClient) GrantTypes() []oidc.GrantType { func (c *ConfClient) GrantTypes() []oidc.GrantType {
return c.grantTypes return c.grantTypes
} }
func (c *ConfClient) DevMode() bool { func (c *ConfClient) DevMode() bool {
return c.devMode return c.devMode
} }
func (c *ConfClient) AllowedScopes() []string { func (c *ConfClient) AllowedScopes() []string {
return nil return nil
} }
func (c *ConfClient) RestrictAdditionalIdTokenScopes() func(scopes []string) []string { func (c *ConfClient) RestrictAdditionalIdTokenScopes() func(scopes []string) []string {
return func(scopes []string) []string { return func(scopes []string) []string {
return scopes return scopes
} }
} }
func (c *ConfClient) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string { func (c *ConfClient) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string {
return func(scopes []string) []string { return func(scopes []string) []string {
return scopes return scopes
} }
} }
func (c *ConfClient) IsScopeAllowed(scope string) bool { func (c *ConfClient) IsScopeAllowed(scope string) bool {
return false return false
} }

View file

@ -29,17 +29,15 @@ const (
defaultKeysEndpoint = "keys" defaultKeysEndpoint = "keys"
) )
var ( var DefaultEndpoints = &endpoints{
DefaultEndpoints = &endpoints{ Authorization: NewEndpoint(defaultAuthorizationEndpoint),
Authorization: NewEndpoint(defaultAuthorizationEndpoint), Token: NewEndpoint(defaultTokenEndpoint),
Token: NewEndpoint(defaultTokenEndpoint), Introspection: NewEndpoint(defaultIntrospectEndpoint),
Introspection: NewEndpoint(defaultIntrospectEndpoint), Userinfo: NewEndpoint(defaultUserinfoEndpoint),
Userinfo: NewEndpoint(defaultUserinfoEndpoint), Revocation: NewEndpoint(defaultRevocationEndpoint),
Revocation: NewEndpoint(defaultRevocationEndpoint), EndSession: NewEndpoint(defaultEndSessionEndpoint),
EndSession: NewEndpoint(defaultEndSessionEndpoint), JwksURI: NewEndpoint(defaultKeysEndpoint),
JwksURI: NewEndpoint(defaultKeysEndpoint), }
}
)
type OpenIDProvider interface { type OpenIDProvider interface {
Configuration Configuration
@ -83,7 +81,7 @@ func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router
return router return router
} }
//AuthCallbackURL builds the url for the redirect (with the requestID) after a successful login // AuthCallbackURL builds the url for the redirect (with the requestID) after a successful login
func AuthCallbackURL(o OpenIDProvider) func(string) string { func AuthCallbackURL(o OpenIDProvider) func(string) string {
return func(requestID string) string { return func(requestID string) string {
return o.AuthorizationEndpoint().Absolute(o.Issuer()) + authCallbackPathSuffix + "?id=" + requestID return o.AuthorizationEndpoint().Absolute(o.Issuer()) + authCallbackPathSuffix + "?id=" + requestID
@ -117,8 +115,8 @@ type endpoints struct {
JwksURI Endpoint JwksURI Endpoint
} }
//NewOpenIDProvider creates a provider. The provider provides (with HttpHandler()) // NewOpenIDProvider creates a provider. The provider provides (with HttpHandler())
//a http.Router that handles a suite of endpoints (some paths can be overridden): // a http.Router that handles a suite of endpoints (some paths can be overridden):
// /healthz // /healthz
// /ready // /ready
// /.well-known/openid-configuration // /.well-known/openid-configuration
@ -130,10 +128,10 @@ type endpoints struct {
// /revoke // /revoke
// /end_session // /end_session
// /keys // /keys
//This does not include login. Login is handled with a redirect that includes the // This does not include login. Login is handled with a redirect that includes the
//request ID. The redirect for logins is specified per-client by Client.LoginURL(). // request ID. The redirect for logins is specified per-client by Client.LoginURL().
//Successful logins should mark the request as authorized and redirect back to to // Successful logins should mark the request as authorized and redirect back to to
//op.AuthCallbackURL(provider) which is probably /callback. On the redirect back // op.AuthCallbackURL(provider) which is probably /callback. On the redirect back
// to the AuthCallbackURL, the request id should be passed as the "id" parameter. // to the AuthCallbackURL, the request id should be passed as the "id" parameter.
func NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opOpts ...Option) (OpenIDProvider, error) { func NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opOpts ...Option) (OpenIDProvider, error) {
err := ValidateIssuer(config.Issuer) err := ValidateIssuer(config.Issuer)
@ -354,8 +352,8 @@ type openIDKeySet struct {
Storage Storage
} }
//VerifySignature implements the oidc.KeySet interface // VerifySignature implements the oidc.KeySet interface
//providing an implementation for the keys stored in the OP Storage interface // providing an implementation for the keys stored in the OP Storage interface
func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) { func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
keySet, err := o.Storage.GetKeySet(ctx) keySet, err := o.Storage.GetKeySet(ctx)
if err != nil { if err != nil {

View file

@ -39,6 +39,7 @@ func ReadySigner(s Signer) ProbesFn {
return s.Health(ctx) return s.Health(ctx)
} }
} }
func ReadyStorage(s Storage) ProbesFn { func ReadyStorage(s Storage) ProbesFn {
return func(ctx context.Context) error { return func(ctx context.Context) error {
if s == nil { if s == nil {

View file

@ -9,8 +9,8 @@ import (
"github.com/zitadel/oidc/pkg/oidc" "github.com/zitadel/oidc/pkg/oidc"
) )
//ClientCredentialsExchange handles the OAuth 2.0 client_credentials grant, including // ClientCredentialsExchange handles the OAuth 2.0 client_credentials grant, including
//parsing, validating, authorizing the client and finally returning a token // parsing, validating, authorizing the client and finally returning a token
func ClientCredentialsExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) { func ClientCredentialsExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
request, err := ParseClientCredentialsRequest(r, exchanger.Decoder()) request, err := ParseClientCredentialsRequest(r, exchanger.Decoder())
if err != nil { if err != nil {
@ -32,7 +32,7 @@ func ClientCredentialsExchange(w http.ResponseWriter, r *http.Request, exchanger
httphelper.MarshalJSON(w, resp) httphelper.MarshalJSON(w, resp)
} }
//ParseClientCredentialsRequest parsed the http request into a oidc.ClientCredentialsRequest // ParseClientCredentialsRequest parsed the http request into a oidc.ClientCredentialsRequest
func ParseClientCredentialsRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.ClientCredentialsRequest, error) { func ParseClientCredentialsRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.ClientCredentialsRequest, error) {
err := r.ParseForm() err := r.ParseForm()
if err != nil { if err != nil {
@ -63,8 +63,8 @@ func ParseClientCredentialsRequest(r *http.Request, decoder httphelper.Decoder)
return request, nil return request, nil
} }
//ValidateClientCredentialsRequest validates the refresh_token request parameters including authorization check of the client // ValidateClientCredentialsRequest validates the refresh_token request parameters including authorization check of the client
//and returns the data representing the original auth request corresponding to the refresh_token // and returns the data representing the original auth request corresponding to the refresh_token
func ValidateClientCredentialsRequest(ctx context.Context, request *oidc.ClientCredentialsRequest, exchanger Exchanger) (TokenRequest, Client, error) { func ValidateClientCredentialsRequest(ctx context.Context, request *oidc.ClientCredentialsRequest, exchanger Exchanger) (TokenRequest, Client, error) {
storage, ok := exchanger.Storage().(ClientCredentialsStorage) storage, ok := exchanger.Storage().(ClientCredentialsStorage)
if !ok { if !ok {

View file

@ -8,8 +8,8 @@ import (
"github.com/zitadel/oidc/pkg/oidc" "github.com/zitadel/oidc/pkg/oidc"
) )
//CodeExchange handles the OAuth 2.0 authorization_code grant, including // CodeExchange handles the OAuth 2.0 authorization_code grant, including
//parsing, validating, authorizing the client and finally exchanging the code for tokens // parsing, validating, authorizing the client and finally exchanging the code for tokens
func CodeExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) { func CodeExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
tokenReq, err := ParseAccessTokenRequest(r, exchanger.Decoder()) tokenReq, err := ParseAccessTokenRequest(r, exchanger.Decoder())
if err != nil { if err != nil {
@ -32,7 +32,7 @@ func CodeExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
httphelper.MarshalJSON(w, resp) httphelper.MarshalJSON(w, resp)
} }
//ParseAccessTokenRequest parsed the http request into a oidc.AccessTokenRequest // ParseAccessTokenRequest parsed the http request into a oidc.AccessTokenRequest
func ParseAccessTokenRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.AccessTokenRequest, error) { func ParseAccessTokenRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.AccessTokenRequest, error) {
request := new(oidc.AccessTokenRequest) request := new(oidc.AccessTokenRequest)
err := ParseAuthenticatedTokenRequest(r, decoder, request) err := ParseAuthenticatedTokenRequest(r, decoder, request)
@ -42,8 +42,8 @@ func ParseAccessTokenRequest(r *http.Request, decoder httphelper.Decoder) (*oidc
return request, nil return request, nil
} }
//ValidateAccessTokenRequest validates the token request parameters including authorization check of the client // ValidateAccessTokenRequest validates the token request parameters including authorization check of the client
//and returns the previous created auth request corresponding to the auth code // and returns the previous created auth request corresponding to the auth code
func ValidateAccessTokenRequest(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (AuthRequest, Client, error) { func ValidateAccessTokenRequest(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (AuthRequest, Client, error) {
authReq, client, err := AuthorizeCodeClient(ctx, tokenReq, exchanger) authReq, client, err := AuthorizeCodeClient(ctx, tokenReq, exchanger)
if err != nil { if err != nil {
@ -61,8 +61,8 @@ func ValidateAccessTokenRequest(ctx context.Context, tokenReq *oidc.AccessTokenR
return authReq, client, nil return authReq, client, nil
} }
//AuthorizeCodeClient checks the authorization of the client and that the used method was the one previously registered. // AuthorizeCodeClient checks the authorization of the client and that the used method was the one previously registered.
//It than returns the auth request corresponding to the auth code // It than returns the auth request corresponding to the auth code
func AuthorizeCodeClient(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (request AuthRequest, client Client, err error) { func AuthorizeCodeClient(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (request AuthRequest, client Client, err error) {
if tokenReq.ClientAssertionType == oidc.ClientAssertionTypeJWTAssertion { if tokenReq.ClientAssertionType == oidc.ClientAssertionTypeJWTAssertion {
jwtExchanger, ok := exchanger.(JWTAuthorizationGrantExchanger) jwtExchanger, ok := exchanger.(JWTAuthorizationGrantExchanger)
@ -102,7 +102,7 @@ func AuthorizeCodeClient(ctx context.Context, tokenReq *oidc.AccessTokenRequest,
return request, client, err return request, client, err
} }
//AuthRequestByCode returns the AuthRequest previously created from Storage corresponding to the auth code or an error // AuthRequestByCode returns the AuthRequest previously created from Storage corresponding to the auth code or an error
func AuthRequestByCode(ctx context.Context, storage Storage, code string) (AuthRequest, error) { func AuthRequestByCode(ctx context.Context, storage Storage, code string) (AuthRequest, error) {
authReq, err := storage.AuthRequestByCode(ctx, code) authReq, err := storage.AuthRequestByCode(ctx, code)
if err != nil { if err != nil {

View file

@ -5,7 +5,7 @@ import (
"net/http" "net/http"
) )
//TokenExchange will handle the OAuth 2.0 token exchange grant ("urn:ietf:params:oauth:grant-type:token-exchange") // TokenExchange will handle the OAuth 2.0 token exchange grant ("urn:ietf:params:oauth:grant-type:token-exchange")
func TokenExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) { func TokenExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
RequestError(w, r, errors.New("unimplemented")) RequestError(w, r, errors.New("unimplemented"))
} }

View file

@ -14,7 +14,7 @@ type JWTAuthorizationGrantExchanger interface {
JWTProfileVerifier() JWTProfileVerifier JWTProfileVerifier() JWTProfileVerifier
} }
//JWTProfile handles the OAuth 2.0 JWT Profile Authorization Grant https://tools.ietf.org/html/rfc7523#section-2.1 // JWTProfile handles the OAuth 2.0 JWT Profile Authorization Grant https://tools.ietf.org/html/rfc7523#section-2.1
func JWTProfile(w http.ResponseWriter, r *http.Request, exchanger JWTAuthorizationGrantExchanger) { func JWTProfile(w http.ResponseWriter, r *http.Request, exchanger JWTAuthorizationGrantExchanger) {
profileRequest, err := ParseJWTProfileGrantRequest(r, exchanger.Decoder()) profileRequest, err := ParseJWTProfileGrantRequest(r, exchanger.Decoder())
if err != nil { if err != nil {
@ -53,7 +53,7 @@ func ParseJWTProfileGrantRequest(r *http.Request, decoder httphelper.Decoder) (*
return tokenReq, nil return tokenReq, nil
} }
//CreateJWTTokenResponse creates // CreateJWTTokenResponse creates
func CreateJWTTokenResponse(ctx context.Context, tokenRequest TokenRequest, creator TokenCreator) (*oidc.AccessTokenResponse, error) { func CreateJWTTokenResponse(ctx context.Context, tokenRequest TokenRequest, creator TokenCreator) (*oidc.AccessTokenResponse, error) {
id, exp, err := creator.Storage().CreateAccessToken(ctx, tokenRequest) id, exp, err := creator.Storage().CreateAccessToken(ctx, tokenRequest)
if err != nil { if err != nil {
@ -71,7 +71,7 @@ func CreateJWTTokenResponse(ctx context.Context, tokenRequest TokenRequest, crea
}, nil }, nil
} }
//ParseJWTProfileRequest has been renamed to ParseJWTProfileGrantRequest // ParseJWTProfileRequest has been renamed to ParseJWTProfileGrantRequest
// //
//deprecated: use ParseJWTProfileGrantRequest //deprecated: use ParseJWTProfileGrantRequest
func ParseJWTProfileRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.JWTProfileGrantRequest, error) { func ParseJWTProfileRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.JWTProfileGrantRequest, error) {

View file

@ -21,8 +21,8 @@ type RefreshTokenRequest interface {
SetCurrentScopes(scopes []string) SetCurrentScopes(scopes []string)
} }
//RefreshTokenExchange handles the OAuth 2.0 refresh_token grant, including // RefreshTokenExchange handles the OAuth 2.0 refresh_token grant, including
//parsing, validating, authorizing the client and finally exchanging the refresh_token for new tokens // parsing, validating, authorizing the client and finally exchanging the refresh_token for new tokens
func RefreshTokenExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) { func RefreshTokenExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
tokenReq, err := ParseRefreshTokenRequest(r, exchanger.Decoder()) tokenReq, err := ParseRefreshTokenRequest(r, exchanger.Decoder())
if err != nil { if err != nil {
@ -41,7 +41,7 @@ func RefreshTokenExchange(w http.ResponseWriter, r *http.Request, exchanger Exch
httphelper.MarshalJSON(w, resp) httphelper.MarshalJSON(w, resp)
} }
//ParseRefreshTokenRequest parsed the http request into a oidc.RefreshTokenRequest // ParseRefreshTokenRequest parsed the http request into a oidc.RefreshTokenRequest
func ParseRefreshTokenRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.RefreshTokenRequest, error) { func ParseRefreshTokenRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.RefreshTokenRequest, error) {
request := new(oidc.RefreshTokenRequest) request := new(oidc.RefreshTokenRequest)
err := ParseAuthenticatedTokenRequest(r, decoder, request) err := ParseAuthenticatedTokenRequest(r, decoder, request)
@ -51,8 +51,8 @@ func ParseRefreshTokenRequest(r *http.Request, decoder httphelper.Decoder) (*oid
return request, nil return request, nil
} }
//ValidateRefreshTokenRequest validates the refresh_token request parameters including authorization check of the client // ValidateRefreshTokenRequest validates the refresh_token request parameters including authorization check of the client
//and returns the data representing the original auth request corresponding to the refresh_token // and returns the data representing the original auth request corresponding to the refresh_token
func ValidateRefreshTokenRequest(ctx context.Context, tokenReq *oidc.RefreshTokenRequest, exchanger Exchanger) (RefreshTokenRequest, Client, error) { func ValidateRefreshTokenRequest(ctx context.Context, tokenReq *oidc.RefreshTokenRequest, exchanger Exchanger) (RefreshTokenRequest, Client, error) {
if tokenReq.RefreshToken == "" { if tokenReq.RefreshToken == "" {
return nil, nil, oidc.ErrInvalidRequest().WithDescription("refresh_token missing") return nil, nil, oidc.ErrInvalidRequest().WithDescription("refresh_token missing")
@ -70,9 +70,9 @@ func ValidateRefreshTokenRequest(ctx context.Context, tokenReq *oidc.RefreshToke
return request, client, nil return request, client, nil
} }
//ValidateRefreshTokenScopes validates that the requested scope is a subset of the original auth request scope // ValidateRefreshTokenScopes validates that the requested scope is a subset of the original auth request scope
//it will set the requested scopes as current scopes onto RefreshTokenRequest // it will set the requested scopes as current scopes onto RefreshTokenRequest
//if empty the original scopes will be used // if empty the original scopes will be used
func ValidateRefreshTokenScopes(requestedScopes []string, authRequest RefreshTokenRequest) error { func ValidateRefreshTokenScopes(requestedScopes []string, authRequest RefreshTokenRequest) error {
if len(requestedScopes) == 0 { if len(requestedScopes) == 0 {
return nil return nil
@ -86,8 +86,8 @@ func ValidateRefreshTokenScopes(requestedScopes []string, authRequest RefreshTok
return nil return nil
} }
//AuthorizeRefreshClient checks the authorization of the client and that the used method was the one previously registered. // AuthorizeRefreshClient checks the authorization of the client and that the used method was the one previously registered.
//It than returns the data representing the original auth request corresponding to the refresh_token // It than returns the data representing the original auth request corresponding to the refresh_token
func AuthorizeRefreshClient(ctx context.Context, tokenReq *oidc.RefreshTokenRequest, exchanger Exchanger) (request RefreshTokenRequest, client Client, err error) { func AuthorizeRefreshClient(ctx context.Context, tokenReq *oidc.RefreshTokenRequest, exchanger Exchanger) (request RefreshTokenRequest, client Client, err error) {
if tokenReq.ClientAssertionType == oidc.ClientAssertionTypeJWTAssertion { if tokenReq.ClientAssertionType == oidc.ClientAssertionTypeJWTAssertion {
jwtExchanger, ok := exchanger.(JWTAuthorizationGrantExchanger) jwtExchanger, ok := exchanger.(JWTAuthorizationGrantExchanger)
@ -128,8 +128,8 @@ func AuthorizeRefreshClient(ctx context.Context, tokenReq *oidc.RefreshTokenRequ
return request, client, err return request, client, err
} }
//RefreshTokenRequestByRefreshToken returns the RefreshTokenRequest (data representing the original auth request) // RefreshTokenRequestByRefreshToken returns the RefreshTokenRequest (data representing the original auth request)
//corresponding to the refresh_token from Storage or an error // corresponding to the refresh_token from Storage or an error
func RefreshTokenRequestByRefreshToken(ctx context.Context, storage Storage, refreshToken string) (RefreshTokenRequest, error) { func RefreshTokenRequestByRefreshToken(ctx context.Context, storage Storage, refreshToken string) (RefreshTokenRequest, error) {
request, err := storage.TokenRequestByRefreshToken(ctx, refreshToken) request, err := storage.TokenRequestByRefreshToken(ctx, refreshToken)
if err != nil { if err != nil {

View file

@ -29,7 +29,7 @@ func tokenHandler(exchanger Exchanger) func(w http.ResponseWriter, r *http.Reque
} }
} }
//Exchange performs a token exchange appropriate for the grant type // Exchange performs a token exchange appropriate for the grant type
func Exchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) { func Exchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
grantType := r.FormValue("grant_type") grantType := r.FormValue("grant_type")
switch grantType { switch grantType {
@ -63,15 +63,15 @@ func Exchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
RequestError(w, r, oidc.ErrUnsupportedGrantType().WithDescription("%s not supported", grantType)) RequestError(w, r, oidc.ErrUnsupportedGrantType().WithDescription("%s not supported", grantType))
} }
//AuthenticatedTokenRequest is a helper interface for ParseAuthenticatedTokenRequest // AuthenticatedTokenRequest is a helper interface for ParseAuthenticatedTokenRequest
//it is implemented by oidc.AuthRequest and oidc.RefreshTokenRequest // it is implemented by oidc.AuthRequest and oidc.RefreshTokenRequest
type AuthenticatedTokenRequest interface { type AuthenticatedTokenRequest interface {
SetClientID(string) SetClientID(string)
SetClientSecret(string) SetClientSecret(string)
} }
//ParseAuthenticatedTokenRequest parses the client_id and client_secret from the HTTP request from either // ParseAuthenticatedTokenRequest parses the client_id and client_secret from the HTTP request from either
//HTTP Basic Auth header or form body and sets them into the provided authenticatedTokenRequest interface // HTTP Basic Auth header or form body and sets them into the provided authenticatedTokenRequest interface
func ParseAuthenticatedTokenRequest(r *http.Request, decoder httphelper.Decoder, request AuthenticatedTokenRequest) error { func ParseAuthenticatedTokenRequest(r *http.Request, decoder httphelper.Decoder, request AuthenticatedTokenRequest) error {
err := r.ParseForm() err := r.ParseForm()
if err != nil { if err != nil {
@ -98,7 +98,7 @@ func ParseAuthenticatedTokenRequest(r *http.Request, decoder httphelper.Decoder,
return nil return nil
} }
//AuthorizeClientIDSecret authorizes a client by validating the client_id and client_secret (Basic Auth and POST) // AuthorizeClientIDSecret authorizes a client by validating the client_id and client_secret (Basic Auth and POST)
func AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string, storage Storage) error { func AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string, storage Storage) error {
err := storage.AuthorizeClientIDSecret(ctx, clientID, clientSecret) err := storage.AuthorizeClientIDSecret(ctx, clientID, clientSecret)
if err != nil { if err != nil {
@ -107,8 +107,8 @@ func AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string,
return nil return nil
} }
//AuthorizeCodeChallenge authorizes a client by validating the code_verifier against the previously sent // AuthorizeCodeChallenge authorizes a client by validating the code_verifier against the previously sent
//code_challenge of the auth request (PKCE) // code_challenge of the auth request (PKCE)
func AuthorizeCodeChallenge(tokenReq *oidc.AccessTokenRequest, challenge *oidc.CodeChallenge) error { func AuthorizeCodeChallenge(tokenReq *oidc.AccessTokenRequest, challenge *oidc.CodeChallenge) error {
if tokenReq.CodeVerifier == "" { if tokenReq.CodeVerifier == "" {
return oidc.ErrInvalidRequest().WithDescription("code_challenge required") return oidc.ErrInvalidRequest().WithDescription("code_challenge required")
@ -119,8 +119,8 @@ func AuthorizeCodeChallenge(tokenReq *oidc.AccessTokenRequest, challenge *oidc.C
return nil return nil
} }
//AuthorizePrivateJWTKey authorizes a client by validating the client_assertion's signature with a previously // AuthorizePrivateJWTKey authorizes a client by validating the client_assertion's signature with a previously
//registered public key (JWT Profile) // registered public key (JWT Profile)
func AuthorizePrivateJWTKey(ctx context.Context, clientAssertion string, exchanger JWTAuthorizationGrantExchanger) (Client, error) { func AuthorizePrivateJWTKey(ctx context.Context, clientAssertion string, exchanger JWTAuthorizationGrantExchanger) (Client, error) {
jwtReq, err := VerifyJWTAssertion(ctx, clientAssertion, exchanger.JWTProfileVerifier()) jwtReq, err := VerifyJWTAssertion(ctx, clientAssertion, exchanger.JWTProfileVerifier())
if err != nil { if err != nil {
@ -136,7 +136,7 @@ func AuthorizePrivateJWTKey(ctx context.Context, clientAssertion string, exchang
return client, nil return client, nil
} }
//ValidateGrantType ensures that the requested grant_type is allowed by the Client // ValidateGrantType ensures that the requested grant_type is allowed by the Client
func ValidateGrantType(client Client, grantType oidc.GrantType) bool { func ValidateGrantType(client Client, grantType oidc.GrantType) bool {
if client == nil { if client == nil {
return false return false

View file

@ -54,9 +54,9 @@ func ParseTokenRevocationRequest(r *http.Request, revoker Revoker) (token, token
} }
req := new(struct { req := new(struct {
oidc.RevocationRequest oidc.RevocationRequest
oidc.ClientAssertionParams //for auth_method private_key_jwt oidc.ClientAssertionParams // for auth_method private_key_jwt
ClientID string `schema:"client_id"` //for auth_method none and post ClientID string `schema:"client_id"` // for auth_method none and post
ClientSecret string `schema:"client_secret"` //for auth_method post ClientSecret string `schema:"client_secret"` // for auth_method post
}) })
err = revoker.Decoder().Decode(req, r.Form) err = revoker.Decoder().Decode(req, r.Form)
if err != nil { if err != nil {

View file

@ -23,27 +23,27 @@ type accessTokenVerifier struct {
keySet oidc.KeySet keySet oidc.KeySet
} }
//Issuer implements oidc.Verifier interface // Issuer implements oidc.Verifier interface
func (i *accessTokenVerifier) Issuer() string { func (i *accessTokenVerifier) Issuer() string {
return i.issuer return i.issuer
} }
//MaxAgeIAT implements oidc.Verifier interface // MaxAgeIAT implements oidc.Verifier interface
func (i *accessTokenVerifier) MaxAgeIAT() time.Duration { func (i *accessTokenVerifier) MaxAgeIAT() time.Duration {
return i.maxAgeIAT return i.maxAgeIAT
} }
//Offset implements oidc.Verifier interface // Offset implements oidc.Verifier interface
func (i *accessTokenVerifier) Offset() time.Duration { func (i *accessTokenVerifier) Offset() time.Duration {
return i.offset return i.offset
} }
//SupportedSignAlgs implements AccessTokenVerifier interface // SupportedSignAlgs implements AccessTokenVerifier interface
func (i *accessTokenVerifier) SupportedSignAlgs() []string { func (i *accessTokenVerifier) SupportedSignAlgs() []string {
return i.supportedSignAlgs return i.supportedSignAlgs
} }
//KeySet implements AccessTokenVerifier interface // KeySet implements AccessTokenVerifier interface
func (i *accessTokenVerifier) KeySet() oidc.KeySet { func (i *accessTokenVerifier) KeySet() oidc.KeySet {
return i.keySet return i.keySet
} }
@ -67,7 +67,7 @@ func NewAccessTokenVerifier(issuer string, keySet oidc.KeySet, opts ...AccessTok
return verifier return verifier
} }
//VerifyAccessToken validates the access token (issuer, signature and expiration) // VerifyAccessToken validates the access token (issuer, signature and expiration)
func VerifyAccessToken(ctx context.Context, token string, v AccessTokenVerifier) (oidc.AccessTokenClaims, error) { func VerifyAccessToken(ctx context.Context, token string, v AccessTokenVerifier) (oidc.AccessTokenClaims, error) {
claims := oidc.EmptyAccessTokenClaims() claims := oidc.EmptyAccessTokenClaims()

View file

@ -61,7 +61,7 @@ func NewIDTokenHintVerifier(issuer string, keySet oidc.KeySet) IDTokenHintVerifi
return verifier return verifier
} }
//VerifyIDTokenHint validates the id token according to // VerifyIDTokenHint validates the id token according to
//https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation //https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
func VerifyIDTokenHint(ctx context.Context, token string, v IDTokenHintVerifier) (oidc.IDTokenClaims, error) { func VerifyIDTokenHint(ctx context.Context, token string, v IDTokenHintVerifier) (oidc.IDTokenClaims, error) {
claims := oidc.EmptyIDTokenClaims() claims := oidc.EmptyIDTokenClaims()

View file

@ -25,7 +25,7 @@ type jwtProfileVerifier struct {
offset time.Duration offset time.Duration
} }
//NewJWTProfileVerifier creates a oidc.Verifier for JWT Profile assertions (authorization grant and client authentication) // NewJWTProfileVerifier creates a oidc.Verifier for JWT Profile assertions (authorization grant and client authentication)
func NewJWTProfileVerifier(storage jwtProfileKeyStorage, issuer string, maxAgeIAT, offset time.Duration, opts ...JWTProfileVerifierOption) JWTProfileVerifier { func NewJWTProfileVerifier(storage jwtProfileKeyStorage, issuer string, maxAgeIAT, offset time.Duration, opts ...JWTProfileVerifierOption) JWTProfileVerifier {
j := &jwtProfileVerifier{ j := &jwtProfileVerifier{
storage: storage, storage: storage,
@ -70,9 +70,9 @@ func (v *jwtProfileVerifier) CheckSubject(request *oidc.JWTTokenRequest) error {
return v.subjectCheck(request) return v.subjectCheck(request)
} }
//VerifyJWTAssertion verifies the assertion string from JWT Profile (authorization grant and client authentication) // VerifyJWTAssertion verifies the assertion string from JWT Profile (authorization grant and client authentication)
// //
//checks audience, exp, iat, signature and that issuer and sub are the same // checks audience, exp, iat, signature and that issuer and sub are the same
func VerifyJWTAssertion(ctx context.Context, assertion string, v JWTProfileVerifier) (*oidc.JWTTokenRequest, error) { func VerifyJWTAssertion(ctx context.Context, assertion string, v JWTProfileVerifier) (*oidc.JWTTokenRequest, error) {
request := new(oidc.JWTTokenRequest) request := new(oidc.JWTTokenRequest)
payload, err := oidc.ParseToken(assertion, request) payload, err := oidc.ParseToken(assertion, request)
@ -119,7 +119,7 @@ type jwtProfileKeySet struct {
clientID string clientID string
} }
//VerifySignature implements oidc.KeySet by getting the public key from Storage implementation // VerifySignature implements oidc.KeySet by getting the public key from Storage implementation
func (k *jwtProfileKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error) { func (k *jwtProfileKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error) {
keyID, _ := oidc.GetKeyIDAndAlg(jws) keyID, _ := oidc.GetKeyIDAndAlg(jws)
key, err := k.storage.GetKeyByIDAndUserID(ctx, keyID, k.clientID) key, err := k.storage.GetKeyByIDAndUserID(ctx, keyID, k.clientID)