fix(oidc): add detailed trace/debug logs (#3012)

This adds significantly more detailed logging for most OpenID Connect handlers.
This commit is contained in:
James Elliott 2022-03-16 09:55:38 +11:00 committed by GitHub
parent 2c3b507096
commit 9b779569f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 322 additions and 127 deletions

View File

@ -1,142 +1,145 @@
package handlers package handlers
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strings" "strings"
"time" "time"
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/ory/fosite/handler/openid"
"github.com/ory/fosite/token/jwt"
"github.com/authelia/authelia/v4/internal/logging"
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/oidc" "github.com/authelia/authelia/v4/internal/oidc"
"github.com/authelia/authelia/v4/internal/session" "github.com/authelia/authelia/v4/internal/session"
) )
func oidcAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *http.Request) { func oidcAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *http.Request) {
ar, err := ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeRequest(ctx, r) var (
if err != nil { requester fosite.AuthorizeRequester
logging.Logger().Errorf("Error occurred in NewAuthorizeRequest: %+v", err) responder fosite.AuthorizeResponder
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err) client *oidc.InternalClient
authTime time.Time
issuer string
err error
)
if requester, err = ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeRequest(ctx, r); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Authorization Request failed with error: %+v", rfc)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, err)
return return
} }
clientID := ar.GetClient().GetID() clientID := requester.GetClient().GetID()
client, err := ctx.Providers.OpenIDConnect.Store.GetInternalClient(clientID)
if err != nil { ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' is being processed", requester.GetID(), clientID)
err := fmt.Errorf("unable to find related client configuration with name '%s': %v", ar.GetID(), err)
ctx.Logger.Error(err) if client, err = ctx.Providers.OpenIDConnect.Store.GetInternalClient(clientID); err != nil {
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err) if errors.Is(err, fosite.ErrNotFound) {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: client was not found", requester.GetID(), clientID)
} else {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: failed to find client: %+v", requester.GetID(), clientID, err)
}
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, err)
return
}
if issuer, err = ctx.ExternalRootURL(); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred determining issuer: %+v", requester.GetID(), clientID, err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not determine issuer."))
return return
} }
userSession := ctx.GetSession() userSession := ctx.GetSession()
requestedScopes := ar.GetRequestedScopes() requestedScopes := requester.GetRequestedScopes()
requestedAudience := ar.GetRequestedAudience() requestedAudience := requester.GetRequestedAudience()
isAuthInsufficient := !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel) isAuthInsufficient := !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel)
if isAuthInsufficient || (isConsentMissing(userSession.OIDCWorkflowSession, requestedScopes, requestedAudience)) { if isAuthInsufficient || (isConsentMissing(userSession.OIDCWorkflowSession, requestedScopes, requestedAudience)) {
oidcAuthorizeHandleAuthorizationOrConsentInsufficient(ctx, userSession, client, isAuthInsufficient, rw, r, ar) oidcAuthorizeHandleAuthorizationOrConsentInsufficient(ctx, userSession, client, isAuthInsufficient, rw, r, requester, issuer)
return return
} }
extraClaims := oidcGrantRequests(ar, requestedScopes, requestedAudience, &userSession) extraClaims := oidcGrantRequests(requester, requestedScopes, requestedAudience, &userSession)
workflowCreated := time.Unix(userSession.OIDCWorkflowSession.CreatedTimestamp, 0) workflowCreated := time.Unix(userSession.OIDCWorkflowSession.CreatedTimestamp, 0)
userSession.OIDCWorkflowSession = nil userSession.OIDCWorkflowSession = nil
if err := ctx.SaveSession(userSession); err != nil {
ctx.Logger.Error(err) if err = ctx.SaveSession(userSession); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred saving session: %+v", requester.GetID(), client.GetID(), err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not save the session."))
return return
} }
issuer, err := ctx.ExternalRootURL() if authTime, err = userSession.AuthenticatedTime(client.Policy); err != nil {
if err != nil { ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred checking authentication time: %+v", requester.GetID(), client.GetID(), err)
ctx.Logger.Errorf("Error occurred obtaining issuer: %+v", err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err) ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not obtain the authentication time."))
return return
} }
authTime, err := userSession.AuthenticatedTime(client.Policy) ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' was successfully processed, proceeding to build Authorization Response", requester.GetID(), clientID)
if err != nil {
ctx.Logger.Errorf("Error occurred obtaining authentication timestamp: %+v", err) subject := userSession.Username
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err) oidcSession := oidc.NewSessionWithAuthorizeRequest(issuer, ctx.Providers.OpenIDConnect.KeyManager.GetActiveKeyID(),
subject, userSession.Username, extraClaims, authTime, workflowCreated, requester)
ctx.Logger.Tracef("Authorization Request with id '%s' on client with id '%s' creating session for Authorization Response for subject '%s' with username '%s' with claims: %+v",
requester.GetID(), oidcSession.ClientID, oidcSession.Subject, oidcSession.Username, oidcSession.Claims)
ctx.Logger.Tracef("Authorization Request with id '%s' on client with id '%s' creating session for Authorization Response for subject '%s' with username '%s' with headers: %+v",
requester.GetID(), oidcSession.ClientID, oidcSession.Subject, oidcSession.Username, oidcSession.Headers)
if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeResponse(ctx, requester, oidcSession); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Authorization Response for Request with id '%s' on client with id '%s' could not be created: %+v", requester.GetID(), clientID, rfc)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, err)
return return
} }
response, err := ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeResponse(ctx, ar, &oidc.OpenIDSession{ ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeResponse(rw, requester, responder)
DefaultSession: &openid.DefaultSession{
Claims: &jwt.IDTokenClaims{
Subject: userSession.Username,
Issuer: issuer,
AuthTime: authTime,
RequestedAt: workflowCreated,
IssuedAt: time.Now(),
Nonce: ar.GetRequestForm().Get("nonce"),
Audience: ar.GetGrantedAudience(),
Extra: extraClaims,
},
Headers: &jwt.Headers{Extra: map[string]interface{}{
"kid": ctx.Providers.OpenIDConnect.KeyManager.GetActiveKeyID(),
}},
Subject: userSession.Username,
Username: userSession.Username,
},
ClientID: clientID,
})
if err != nil {
ctx.Logger.Errorf("Error occurred in NewAuthorizeResponse: %+v", err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err)
return
}
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeResponse(rw, ar, response)
} }
func oidcAuthorizeHandleAuthorizationOrConsentInsufficient( func oidcAuthorizeHandleAuthorizationOrConsentInsufficient(
ctx *middlewares.AutheliaCtx, userSession session.UserSession, client *oidc.InternalClient, isAuthInsufficient bool, ctx *middlewares.AutheliaCtx, userSession session.UserSession, client *oidc.InternalClient, isAuthInsufficient bool,
rw http.ResponseWriter, r *http.Request, rw http.ResponseWriter, r *http.Request,
ar fosite.AuthorizeRequester) { requester fosite.AuthorizeRequester, issuer string) {
issuer, err := ctx.ExternalRootURL()
if err != nil {
ctx.Logger.Error(err)
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
redirectURL := fmt.Sprintf("%s%s", issuer, string(ctx.Request.RequestURI())) redirectURL := fmt.Sprintf("%s%s", issuer, string(ctx.Request.RequestURI()))
ctx.Logger.Debugf("User %s must consent with scopes %s", ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' requires user '%s' provides consent for scopes '%s'",
userSession.Username, strings.Join(ar.GetRequestedScopes(), ", ")) requester.GetID(), client.GetID(), userSession.Username, strings.Join(requester.GetRequestedScopes(), "', '"))
userSession.OIDCWorkflowSession = &session.OIDCWorkflowSession{ userSession.OIDCWorkflowSession = &session.OIDCWorkflowSession{
ClientID: client.ID, ClientID: client.GetID(),
RequestedScopes: ar.GetRequestedScopes(), RequestedScopes: requester.GetRequestedScopes(),
RequestedAudience: ar.GetRequestedAudience(), RequestedAudience: requester.GetRequestedAudience(),
AuthURI: redirectURL, AuthURI: redirectURL,
TargetURI: ar.GetRedirectURI().String(), TargetURI: requester.GetRedirectURI().String(),
RequiredAuthorizationLevel: client.Policy, RequiredAuthorizationLevel: client.Policy,
CreatedTimestamp: time.Now().Unix(), CreatedTimestamp: time.Now().Unix(),
} }
if err := ctx.SaveSession(userSession); err != nil { if err := ctx.SaveSession(userSession); err != nil {
ctx.Logger.Errorf("Unable to save session: %v", err) ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred saving session for consent: %+v", requester.GetID(), client.GetID(), err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not save the session."))
return return
} }

View File

@ -3,20 +3,33 @@ package handlers
import ( import (
"net/http" "net/http"
"github.com/ory/fosite"
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/oidc"
) )
func oidcIntrospection(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) { func oidcIntrospection(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) {
oidcSession := newOpenIDSession("") var (
responder fosite.IntrospectionResponder
err error
)
ir, err := ctx.Providers.OpenIDConnect.Fosite.NewIntrospectionRequest(ctx, req, oidcSession) oidcSession := oidc.NewSession()
if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewIntrospectionRequest(ctx, req, oidcSession); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Introspection Request failed with error: %+v", rfc)
if err != nil {
ctx.Logger.Errorf("Error occurred in NewIntrospectionRequest: %+v", err)
ctx.Providers.OpenIDConnect.Fosite.WriteIntrospectionError(rw, err) ctx.Providers.OpenIDConnect.Fosite.WriteIntrospectionError(rw, err)
return return
} }
ctx.Providers.OpenIDConnect.Fosite.WriteIntrospectionResponse(rw, ir) requester := responder.GetAccessRequester()
ctx.Logger.Tracef("Introspection Request yeilded a %s (active: %t) requested at %s created with request id '%s' on client with id '%s'", responder.GetTokenUse(), responder.IsActive(), requester.GetRequestedAt().String(), requester.GetID(), requester.GetClient().GetID())
ctx.Providers.OpenIDConnect.Fosite.WriteIntrospectionResponse(rw, responder)
} }

View File

@ -10,6 +10,6 @@ func oidcJWKs(ctx *middlewares.AutheliaCtx) {
ctx.SetContentType("application/json") ctx.SetContentType("application/json")
if err := json.NewEncoder(ctx).Encode(ctx.Providers.OpenIDConnect.KeyManager.GetKeySet()); err != nil { if err := json.NewEncoder(ctx).Encode(ctx.Providers.OpenIDConnect.KeyManager.GetKeySet()); err != nil {
ctx.Error(err, "failed to serve jwk set") ctx.Error(err, "failed to serve json web key set")
} }
} }

View File

@ -3,11 +3,19 @@ package handlers
import ( import (
"net/http" "net/http"
"github.com/ory/fosite"
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
) )
func oidcRevocation(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) { func oidcRevocation(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) {
err := ctx.Providers.OpenIDConnect.Fosite.NewRevocationRequest(ctx, req) var err error
if err = ctx.Providers.OpenIDConnect.Fosite.NewRevocationRequest(ctx, req); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Revocation Request failed with error: %+v", rfc)
}
ctx.Providers.OpenIDConnect.Fosite.WriteRevocationResponse(rw, err) ctx.Providers.OpenIDConnect.Fosite.WriteRevocationResponse(rw, err)
} }

View File

@ -6,35 +6,54 @@ import (
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/oidc"
) )
func oidcToken(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) { func oidcToken(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) {
oidcSession := newOpenIDSession("") var (
requester fosite.AccessRequester
responder fosite.AccessResponder
err error
)
accessRequest, err := ctx.Providers.OpenIDConnect.Fosite.NewAccessRequest(ctx, req, oidcSession) oidcSession := oidc.NewSession()
if err != nil {
ctx.Logger.Errorf("Error occurred in NewAccessRequest: %+v", err) if requester, err = ctx.Providers.OpenIDConnect.Fosite.NewAccessRequest(ctx, req, oidcSession); err != nil {
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, accessRequest, err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Access Request failed with error: %+v", rfc)
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, requester, err)
return return
} }
client := requester.GetClient()
ctx.Logger.Debugf("Access Request with id '%s' on client with id '%s' is being processed", requester.GetID(), client.GetID())
// If this is a client_credentials grant, grant all scopes the client is allowed to perform. // If this is a client_credentials grant, grant all scopes the client is allowed to perform.
if accessRequest.GetGrantTypes().ExactOne("client_credentials") { if requester.GetGrantTypes().ExactOne("client_credentials") {
for _, scope := range accessRequest.GetRequestedScopes() { for _, scope := range requester.GetRequestedScopes() {
if fosite.HierarchicScopeStrategy(accessRequest.GetClient().GetScopes(), scope) { if fosite.HierarchicScopeStrategy(client.GetScopes(), scope) {
accessRequest.GrantScope(scope) requester.GrantScope(scope)
} }
} }
} }
response, err := ctx.Providers.OpenIDConnect.Fosite.NewAccessResponse(ctx, accessRequest) if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewAccessResponse(ctx, requester); err != nil {
if err != nil { rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Error occurred in NewAccessResponse: %+v", err)
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, accessRequest, err) ctx.Logger.Errorf("Access Response for Request with id '%s' failed to be created with error: %+v", requester.GetID(), rfc)
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, requester, err)
return return
} }
ctx.Providers.OpenIDConnect.Fosite.WriteAccessResponse(rw, accessRequest, response) ctx.Logger.Debugf("Access Request with id '%s' on client with id '%s' has successfully been processed", requester.GetID(), client.GetID())
ctx.Logger.Tracef("Access Request with id '%s' on client with id '%s' produced the following claims: %+v", requester.GetID(), client.GetID(), responder.ToMap())
ctx.Providers.OpenIDConnect.Fosite.WriteAccessResponse(rw, requester, responder)
} }

View File

@ -15,13 +15,23 @@ import (
) )
func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) { func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) {
session := newOpenIDSession("") var (
tokenType fosite.TokenType
requester fosite.AccessRequester
client *oidc.InternalClient
err error
)
tokenType, ar, err := ctx.Providers.OpenIDConnect.Fosite.IntrospectToken(req.Context(), fosite.AccessTokenFromRequest(req), fosite.AccessToken, session) oidcSession := oidc.NewSession()
if err != nil {
if tokenType, requester, err = ctx.Providers.OpenIDConnect.Fosite.IntrospectToken(
req.Context(), fosite.AccessTokenFromRequest(req), fosite.AccessToken, oidcSession); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("UserInfo Request failed with error: %+v", rfc)
if rfc.StatusCode() == http.StatusUnauthorized { if rfc.StatusCode() == http.StatusUnauthorized {
rw.Header().Set("WWW-Authenticate", fmt.Sprintf("error=%s,error_description=%s", rfc.ErrorField, rfc.GetDescription())) rw.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfc.ErrorField, rfc.GetDescription()))
} }
ctx.Providers.OpenIDConnect.WriteError(rw, req, err) ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
@ -29,22 +39,25 @@ func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *htt
return return
} }
clientID := requester.GetClient().GetID()
if tokenType != fosite.AccessToken { if tokenType != fosite.AccessToken {
errStr := "authorization header must contain an OAuth access token." ctx.Logger.Errorf("UserInfo Request with id '%s' on client with id '%s' failed with error: bearer authorization failed as the token is not an access_token", requester.GetID(), client.GetID())
rw.Header().Set("WWW-Authenticate", fmt.Sprintf("error_description=\"%s\"", errStr))
errStr := "Only access tokens are allowed in the authorization header."
rw.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer error="invalid_token",error_description="%s"`, errStr))
ctx.Providers.OpenIDConnect.WriteErrorCode(rw, req, http.StatusUnauthorized, errors.New(errStr)) ctx.Providers.OpenIDConnect.WriteErrorCode(rw, req, http.StatusUnauthorized, errors.New(errStr))
return return
} }
client, ok := ar.GetClient().(*oidc.InternalClient) if client, err = ctx.Providers.OpenIDConnect.Store.GetInternalClient(clientID); err != nil {
if !ok {
ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHint("Unable to assert type of client"))) ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHint("Unable to assert type of client")))
return return
} }
claims := ar.GetSession().(*oidc.OpenIDSession).IDTokenClaims().ToMap() claims := requester.GetSession().(*oidc.OpenIDSession).IDTokenClaims().ToMap()
delete(claims, "jti") delete(claims, "jti")
delete(claims, "sid") delete(claims, "sid")
delete(claims, "at_hash") delete(claims, "at_hash")
@ -52,27 +65,49 @@ func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *htt
delete(claims, "exp") delete(claims, "exp")
delete(claims, "nonce") delete(claims, "nonce")
if audience, ok := claims["aud"].([]string); !ok || len(audience) == 0 { audience, ok := claims["aud"].([]string)
claims["aud"] = []string{client.GetID()}
if !ok || len(audience) == 0 {
audience = []string{client.GetID()}
} else {
found := false
for _, aud := range audience {
if aud == clientID {
found = true
break
}
}
if found {
audience = append(audience, clientID)
}
} }
claims["aud"] = audience
var (
keyID, token string
)
ctx.Logger.Tracef("UserInfo Response with id '%s' on client with id '%s' is being sent with the following claims: %+v", requester.GetID(), clientID, claims)
switch client.UserinfoSigningAlgorithm { switch client.UserinfoSigningAlgorithm {
case "RS256": case "RS256":
claims["jti"] = uuid.New() claims["jti"] = uuid.New()
claims["iat"] = time.Now().Unix() claims["iat"] = time.Now().Unix()
keyID, err := ctx.Providers.OpenIDConnect.KeyManager.Strategy().GetPublicKeyID(req.Context()) if keyID, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().GetPublicKeyID(req.Context()); err != nil {
if err != nil { ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHintf("Could not find the active JWK."))
ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
return return
} }
token, _, err := ctx.Providers.OpenIDConnect.KeyManager.Strategy().Generate(req.Context(), claims, headers := &jwt.Headers{
&jwt.Headers{ Extra: map[string]interface{}{"kid": keyID},
Extra: map[string]interface{}{"kid": keyID}, }
})
if err != nil { if token, _, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().Generate(req.Context(), claims, headers); err != nil {
ctx.Providers.OpenIDConnect.WriteError(rw, req, err) ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
return return
@ -83,6 +118,6 @@ func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *htt
case "none", "": case "none", "":
ctx.Providers.OpenIDConnect.Write(rw, req, claims) ctx.Providers.OpenIDConnect.Write(rw, req, claims)
default: default:
ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHintf("Unsupported userinfo signing algorithm '%s'.", client.UserinfoSigningAlgorithm))) ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHintf("Unsupported UserInfo signing algorithm '%s'.", client.UserinfoSigningAlgorithm)))
} }
} }

View File

@ -2,8 +2,6 @@ package handlers
import ( import (
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/ory/fosite/handler/openid"
"github.com/ory/fosite/token/jwt"
"github.com/authelia/authelia/v4/internal/oidc" "github.com/authelia/authelia/v4/internal/oidc"
"github.com/authelia/authelia/v4/internal/session" "github.com/authelia/authelia/v4/internal/session"
@ -21,17 +19,6 @@ func isConsentMissing(workflow *session.OIDCWorkflowSession, requestedScopes, re
len(requestedAudience) > 0 && utils.IsStringSlicesDifferentFold(requestedAudience, workflow.GrantedAudience) len(requestedAudience) > 0 && utils.IsStringSlicesDifferentFold(requestedAudience, workflow.GrantedAudience)
} }
func newOpenIDSession(subject string) *oidc.OpenIDSession {
return &oidc.OpenIDSession{
DefaultSession: &openid.DefaultSession{
Claims: new(jwt.IDTokenClaims),
Headers: new(jwt.Headers),
Subject: subject,
},
Extra: map[string]interface{}{},
}
}
func oidcGrantRequests(ar fosite.AuthorizeRequester, scopes, audiences []string, userSession *session.UserSession) (extraClaims map[string]interface{}) { func oidcGrantRequests(ar fosite.AuthorizeRequester, scopes, audiences []string, userSession *session.UserSession) (extraClaims map[string]interface{}) {
extraClaims = map[string]interface{}{} extraClaims = map[string]interface{}{}

View File

@ -2,16 +2,65 @@ package oidc
import ( import (
"crypto/rsa" "crypto/rsa"
"time"
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/ory/fosite/handler/openid" "github.com/ory/fosite/handler/openid"
"github.com/ory/fosite/storage" "github.com/ory/fosite/storage"
"github.com/ory/fosite/token/jwt"
"github.com/ory/herodot" "github.com/ory/herodot"
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
"github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/authorization"
) )
// NewSession creates a new OpenIDSession struct.
func NewSession() (session *OpenIDSession) {
return &OpenIDSession{
DefaultSession: &openid.DefaultSession{
Claims: &jwt.IDTokenClaims{
Extra: map[string]interface{}{},
},
Headers: &jwt.Headers{
Extra: map[string]interface{}{},
},
},
Extra: map[string]interface{}{},
}
}
// NewSessionWithAuthorizeRequest uses details from an AuthorizeRequester to generate an OpenIDSession.
func NewSessionWithAuthorizeRequest(issuer, kid, subject, username string, extra map[string]interface{},
authTime, requestedAt time.Time, requester fosite.AuthorizeRequester) (session *OpenIDSession) {
if extra == nil {
extra = make(map[string]interface{})
}
return &OpenIDSession{
DefaultSession: &openid.DefaultSession{
Claims: &jwt.IDTokenClaims{
Subject: subject,
Issuer: issuer,
AuthTime: authTime,
RequestedAt: requestedAt,
IssuedAt: time.Now(),
Nonce: requester.GetRequestForm().Get("nonce"),
Audience: requester.GetGrantedAudience(),
Extra: extra,
},
Headers: &jwt.Headers{
Extra: map[string]interface{}{
"kid": kid,
},
},
Subject: subject,
Username: username,
},
Extra: map[string]interface{}{},
ClientID: requester.GetClient().GetID(),
}
}
// OpenIDConnectProvider for OpenID Connect. // OpenIDConnectProvider for OpenID Connect.
type OpenIDConnectProvider struct { type OpenIDConnectProvider struct {
Fosite fosite.OAuth2Provider Fosite fosite.OAuth2Provider

View File

@ -0,0 +1,81 @@
package oidc
import (
"net/url"
"testing"
"time"
"github.com/google/uuid"
"github.com/ory/fosite"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewSession(t *testing.T) {
session := NewSession()
require.NotNil(t, session)
assert.Equal(t, "", session.ClientID)
assert.Equal(t, "", session.Username)
assert.Equal(t, "", session.Subject)
require.NotNil(t, session.Claims)
assert.NotNil(t, session.Claims.Extra)
assert.NotNil(t, session.Extra)
require.NotNil(t, session.Headers)
assert.NotNil(t, session.Headers.Extra)
}
func TestNewSessionWithAuthorizeRequest(t *testing.T) {
requestID := uuid.New()
subject := uuid.New()
formValues := url.Values{}
formValues.Set("nonce", "abc123xyzauthelia")
request := &fosite.AuthorizeRequest{
Request: fosite.Request{
ID: requestID.String(),
Form: formValues,
Client: &InternalClient{ID: "example"},
},
}
extra := map[string]interface{}{
"preferred_username": "john",
}
requested := time.Unix(1647332518, 0)
authAt := time.Unix(1647332500, 0)
issuer := "https://example.com"
session := NewSessionWithAuthorizeRequest(issuer, "primary", subject.String(), "john", extra, authAt, requested, request)
require.NotNil(t, session)
require.NotNil(t, session.Extra)
require.NotNil(t, session.Headers)
require.NotNil(t, session.Headers.Extra)
require.NotNil(t, session.Claims)
require.NotNil(t, session.Claims.Extra)
assert.Equal(t, "abc123xyzauthelia", session.Claims.Nonce)
assert.Equal(t, subject.String(), session.Claims.Subject)
assert.Equal(t, subject.String(), session.Subject)
assert.Equal(t, issuer, session.Claims.Issuer)
assert.Equal(t, "primary", session.Headers.Get("kid"))
assert.Equal(t, "example", session.ClientID)
assert.Equal(t, requested, session.Claims.RequestedAt)
assert.Equal(t, authAt, session.Claims.AuthTime)
assert.Greater(t, session.Claims.IssuedAt.Unix(), authAt.Unix())
assert.Equal(t, "john", session.Username)
require.Contains(t, session.Claims.Extra, "preferred_username")
assert.Equal(t, "john", session.Claims.Extra["preferred_username"])
session = NewSessionWithAuthorizeRequest(issuer, "primary", subject.String(), "john", nil, authAt, requested, request)
require.NotNil(t, session)
require.NotNil(t, session.Claims)
assert.NotNil(t, session.Claims.Extra)
}