fix(oidc): add detailed trace/debug logs (#3012)
This adds significantly more detailed logging for most OpenID Connect handlers.pull/3015/head
parent
2c3b507096
commit
9b779569f4
|
@ -1,142 +1,145 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/oidc"
|
||||
"github.com/authelia/authelia/v4/internal/session"
|
||||
)
|
||||
|
||||
func oidcAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *http.Request) {
|
||||
ar, err := ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeRequest(ctx, r)
|
||||
if err != nil {
|
||||
logging.Logger().Errorf("Error occurred in NewAuthorizeRequest: %+v", err)
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err)
|
||||
var (
|
||||
requester fosite.AuthorizeRequester
|
||||
responder fosite.AuthorizeResponder
|
||||
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
|
||||
}
|
||||
|
||||
clientID := ar.GetClient().GetID()
|
||||
client, err := ctx.Providers.OpenIDConnect.Store.GetInternalClient(clientID)
|
||||
clientID := requester.GetClient().GetID()
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable to find related client configuration with name '%s': %v", ar.GetID(), err)
|
||||
ctx.Logger.Error(err)
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err)
|
||||
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' is being processed", requester.GetID(), clientID)
|
||||
|
||||
if client, err = ctx.Providers.OpenIDConnect.Store.GetInternalClient(clientID); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
userSession := ctx.GetSession()
|
||||
|
||||
requestedScopes := ar.GetRequestedScopes()
|
||||
requestedAudience := ar.GetRequestedAudience()
|
||||
requestedScopes := requester.GetRequestedScopes()
|
||||
requestedAudience := requester.GetRequestedAudience()
|
||||
|
||||
isAuthInsufficient := !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
extraClaims := oidcGrantRequests(ar, requestedScopes, requestedAudience, &userSession)
|
||||
extraClaims := oidcGrantRequests(requester, requestedScopes, requestedAudience, &userSession)
|
||||
|
||||
workflowCreated := time.Unix(userSession.OIDCWorkflowSession.CreatedTimestamp, 0)
|
||||
|
||||
userSession.OIDCWorkflowSession = nil
|
||||
if err := ctx.SaveSession(userSession); err != nil {
|
||||
ctx.Logger.Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
|
||||
if err = ctx.SaveSession(userSession); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
issuer, err := ctx.ExternalRootURL()
|
||||
if err != nil {
|
||||
ctx.Logger.Errorf("Error occurred obtaining issuer: %+v", err)
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err)
|
||||
if authTime, err = userSession.AuthenticatedTime(client.Policy); 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.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not obtain the authentication time."))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
authTime, err := userSession.AuthenticatedTime(client.Policy)
|
||||
if err != nil {
|
||||
ctx.Logger.Errorf("Error occurred obtaining authentication timestamp: %+v", err)
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, ar, err)
|
||||
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' was successfully processed, proceeding to build Authorization Response", requester.GetID(), clientID)
|
||||
|
||||
subject := userSession.Username
|
||||
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
|
||||
}
|
||||
|
||||
response, err := ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeResponse(ctx, ar, &oidc.OpenIDSession{
|
||||
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)
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeResponse(rw, requester, responder)
|
||||
}
|
||||
|
||||
func oidcAuthorizeHandleAuthorizationOrConsentInsufficient(
|
||||
ctx *middlewares.AutheliaCtx, userSession session.UserSession, client *oidc.InternalClient, isAuthInsufficient bool,
|
||||
rw http.ResponseWriter, r *http.Request,
|
||||
ar fosite.AuthorizeRequester) {
|
||||
issuer, err := ctx.ExternalRootURL()
|
||||
if err != nil {
|
||||
ctx.Logger.Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
requester fosite.AuthorizeRequester, issuer string) {
|
||||
redirectURL := fmt.Sprintf("%s%s", issuer, string(ctx.Request.RequestURI()))
|
||||
|
||||
ctx.Logger.Debugf("User %s must consent with scopes %s",
|
||||
userSession.Username, strings.Join(ar.GetRequestedScopes(), ", "))
|
||||
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' requires user '%s' provides consent for scopes '%s'",
|
||||
requester.GetID(), client.GetID(), userSession.Username, strings.Join(requester.GetRequestedScopes(), "', '"))
|
||||
|
||||
userSession.OIDCWorkflowSession = &session.OIDCWorkflowSession{
|
||||
ClientID: client.ID,
|
||||
RequestedScopes: ar.GetRequestedScopes(),
|
||||
RequestedAudience: ar.GetRequestedAudience(),
|
||||
ClientID: client.GetID(),
|
||||
RequestedScopes: requester.GetRequestedScopes(),
|
||||
RequestedAudience: requester.GetRequestedAudience(),
|
||||
AuthURI: redirectURL,
|
||||
TargetURI: ar.GetRedirectURI().String(),
|
||||
TargetURI: requester.GetRedirectURI().String(),
|
||||
RequiredAuthorizationLevel: client.Policy,
|
||||
CreatedTimestamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
if err := ctx.SaveSession(userSession); err != nil {
|
||||
ctx.Logger.Errorf("Unable to save session: %v", err)
|
||||
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 for consent: %+v", requester.GetID(), client.GetID(), err)
|
||||
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not save the session."))
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -3,20 +3,33 @@ package handlers
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
|
||||
"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) {
|
||||
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)
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@ func oidcJWKs(ctx *middlewares.AutheliaCtx) {
|
|||
ctx.SetContentType("application/json")
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,19 @@ package handlers
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -6,35 +6,54 @@ import (
|
|||
"github.com/ory/fosite"
|
||||
|
||||
"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) {
|
||||
oidcSession := newOpenIDSession("")
|
||||
var (
|
||||
requester fosite.AccessRequester
|
||||
responder fosite.AccessResponder
|
||||
err error
|
||||
)
|
||||
|
||||
accessRequest, err := ctx.Providers.OpenIDConnect.Fosite.NewAccessRequest(ctx, req, oidcSession)
|
||||
if err != nil {
|
||||
ctx.Logger.Errorf("Error occurred in NewAccessRequest: %+v", err)
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, accessRequest, err)
|
||||
oidcSession := oidc.NewSession()
|
||||
|
||||
if requester, err = ctx.Providers.OpenIDConnect.Fosite.NewAccessRequest(ctx, req, oidcSession); err != nil {
|
||||
rfc := fosite.ErrorToRFC6749Error(err)
|
||||
|
||||
ctx.Logger.Errorf("Access Request failed with error: %+v", rfc)
|
||||
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, requester, err)
|
||||
|
||||
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 accessRequest.GetGrantTypes().ExactOne("client_credentials") {
|
||||
for _, scope := range accessRequest.GetRequestedScopes() {
|
||||
if fosite.HierarchicScopeStrategy(accessRequest.GetClient().GetScopes(), scope) {
|
||||
accessRequest.GrantScope(scope)
|
||||
if requester.GetGrantTypes().ExactOne("client_credentials") {
|
||||
for _, scope := range requester.GetRequestedScopes() {
|
||||
if fosite.HierarchicScopeStrategy(client.GetScopes(), scope) {
|
||||
requester.GrantScope(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response, err := ctx.Providers.OpenIDConnect.Fosite.NewAccessResponse(ctx, accessRequest)
|
||||
if err != nil {
|
||||
ctx.Logger.Errorf("Error occurred in NewAccessResponse: %+v", err)
|
||||
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, accessRequest, err)
|
||||
if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewAccessResponse(ctx, requester); err != nil {
|
||||
rfc := fosite.ErrorToRFC6749Error(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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -15,13 +15,23 @@ import (
|
|||
)
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
oidcSession := oidc.NewSession()
|
||||
|
||||
if tokenType, requester, err = ctx.Providers.OpenIDConnect.Fosite.IntrospectToken(
|
||||
req.Context(), fosite.AccessTokenFromRequest(req), fosite.AccessToken, oidcSession); err != nil {
|
||||
rfc := fosite.ErrorToRFC6749Error(err)
|
||||
|
||||
ctx.Logger.Errorf("UserInfo Request failed with error: %+v", rfc)
|
||||
|
||||
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)
|
||||
|
@ -29,22 +39,25 @@ func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *htt
|
|||
return
|
||||
}
|
||||
|
||||
clientID := requester.GetClient().GetID()
|
||||
|
||||
if tokenType != fosite.AccessToken {
|
||||
errStr := "authorization header must contain an OAuth access token."
|
||||
rw.Header().Set("WWW-Authenticate", fmt.Sprintf("error_description=\"%s\"", errStr))
|
||||
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())
|
||||
|
||||
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))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
client, ok := ar.GetClient().(*oidc.InternalClient)
|
||||
if !ok {
|
||||
if client, err = ctx.Providers.OpenIDConnect.Store.GetInternalClient(clientID); err != nil {
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHint("Unable to assert type of client")))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
claims := ar.GetSession().(*oidc.OpenIDSession).IDTokenClaims().ToMap()
|
||||
claims := requester.GetSession().(*oidc.OpenIDSession).IDTokenClaims().ToMap()
|
||||
delete(claims, "jti")
|
||||
delete(claims, "sid")
|
||||
delete(claims, "at_hash")
|
||||
|
@ -52,27 +65,49 @@ func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *htt
|
|||
delete(claims, "exp")
|
||||
delete(claims, "nonce")
|
||||
|
||||
if audience, ok := claims["aud"].([]string); !ok || len(audience) == 0 {
|
||||
claims["aud"] = []string{client.GetID()}
|
||||
audience, ok := claims["aud"].([]string)
|
||||
|
||||
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 {
|
||||
case "RS256":
|
||||
claims["jti"] = uuid.New()
|
||||
claims["iat"] = time.Now().Unix()
|
||||
|
||||
keyID, err := ctx.Providers.OpenIDConnect.KeyManager.Strategy().GetPublicKeyID(req.Context())
|
||||
if err != nil {
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
|
||||
if keyID, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().GetPublicKeyID(req.Context()); err != nil {
|
||||
ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHintf("Could not find the active JWK."))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
token, _, err := ctx.Providers.OpenIDConnect.KeyManager.Strategy().Generate(req.Context(), claims,
|
||||
&jwt.Headers{
|
||||
headers := &jwt.Headers{
|
||||
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)
|
||||
|
||||
return
|
||||
|
@ -83,6 +118,6 @@ func oidcUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *htt
|
|||
case "none", "":
|
||||
ctx.Providers.OpenIDConnect.Write(rw, req, claims)
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@ package handlers
|
|||
|
||||
import (
|
||||
"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/session"
|
||||
|
@ -21,17 +19,6 @@ func isConsentMissing(workflow *session.OIDCWorkflowSession, requestedScopes, re
|
|||
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{}) {
|
||||
extraClaims = map[string]interface{}{}
|
||||
|
||||
|
|
|
@ -2,16 +2,65 @@ package oidc
|
|||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"time"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/fosite/handler/openid"
|
||||
"github.com/ory/fosite/storage"
|
||||
"github.com/ory/fosite/token/jwt"
|
||||
"github.com/ory/herodot"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"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.
|
||||
type OpenIDConnectProvider struct {
|
||||
Fosite fosite.OAuth2Provider
|
||||
|
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue