2021-07-10 11:56:33 +07:00
package handlers
import (
"fmt"
"net/http"
"time"
"github.com/google/uuid"
"github.com/ory/fosite"
"github.com/ory/fosite/token/jwt"
"github.com/pkg/errors"
2021-08-11 08:04:35 +07:00
"github.com/authelia/authelia/v4/internal/middlewares"
2022-04-07 12:33:53 +07:00
"github.com/authelia/authelia/v4/internal/model"
2021-08-11 08:04:35 +07:00
"github.com/authelia/authelia/v4/internal/oidc"
2021-07-10 11:56:33 +07:00
)
2022-04-07 07:58:51 +07:00
// OpenIDConnectUserinfo handles GET/POST requests to the OpenID Connect 1.0 UserInfo endpoint.
//
// https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
func OpenIDConnectUserinfo ( ctx * middlewares . AutheliaCtx , rw http . ResponseWriter , req * http . Request ) {
2022-03-16 05:55:38 +07:00
var (
tokenType fosite . TokenType
requester fosite . AccessRequester
2022-04-07 12:33:53 +07:00
client * oidc . Client
2022-03-16 05:55:38 +07:00
err error
)
2021-07-10 11:56:33 +07:00
2022-03-16 05:55:38 +07:00
oidcSession := oidc . NewSession ( )
if tokenType , requester , err = ctx . Providers . OpenIDConnect . Fosite . IntrospectToken (
req . Context ( ) , fosite . AccessTokenFromRequest ( req ) , fosite . AccessToken , oidcSession ) ; err != nil {
2021-07-10 11:56:33 +07:00
rfc := fosite . ErrorToRFC6749Error ( err )
2022-03-16 05:55:38 +07:00
ctx . Logger . Errorf ( "UserInfo Request failed with error: %+v" , rfc )
2021-07-10 11:56:33 +07:00
if rfc . StatusCode ( ) == http . StatusUnauthorized {
2022-03-16 05:55:38 +07:00
rw . Header ( ) . Set ( "WWW-Authenticate" , fmt . Sprintf ( ` Bearer error="%s",error_description="%s" ` , rfc . ErrorField , rfc . GetDescription ( ) ) )
2021-07-10 11:56:33 +07:00
}
ctx . Providers . OpenIDConnect . WriteError ( rw , req , err )
return
}
2022-03-16 05:55:38 +07:00
clientID := requester . GetClient ( ) . GetID ( )
2021-07-10 11:56:33 +07:00
if tokenType != fosite . AccessToken {
2022-03-16 05:55:38 +07:00
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 ) )
2021-07-10 11:56:33 +07:00
ctx . Providers . OpenIDConnect . WriteErrorCode ( rw , req , http . StatusUnauthorized , errors . New ( errStr ) )
return
}
2022-04-07 12:33:53 +07:00
if client , err = ctx . Providers . OpenIDConnect . Store . GetFullClient ( clientID ) ; err != nil {
2021-07-10 11:56:33 +07:00
ctx . Providers . OpenIDConnect . WriteError ( rw , req , errors . WithStack ( fosite . ErrServerError . WithHint ( "Unable to assert type of client" ) ) )
return
}
2022-04-07 12:33:53 +07:00
claims := requester . GetSession ( ) . ( * model . OpenIDSession ) . IDTokenClaims ( ) . ToMap ( )
2021-07-10 11:56:33 +07:00
delete ( claims , "jti" )
delete ( claims , "sid" )
delete ( claims , "at_hash" )
delete ( claims , "c_hash" )
delete ( claims , "exp" )
delete ( claims , "nonce" )
2022-03-16 05:55:38 +07:00
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 )
}
2021-07-10 11:56:33 +07:00
}
2022-03-16 05:55:38 +07:00
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 )
2021-07-10 11:56:33 +07:00
switch client . UserinfoSigningAlgorithm {
case "RS256" :
2022-03-16 07:29:46 +07:00
var jti uuid . UUID
if jti , err = uuid . NewRandom ( ) ; err != nil {
2022-04-07 07:58:51 +07:00
ctx . Providers . OpenIDConnect . WriteError ( rw , req , fosite . ErrServerError . WithHintf ( "Could not generate JTI." ) )
2022-03-16 07:29:46 +07:00
return
}
claims [ "jti" ] = jti . String ( )
2021-07-10 11:56:33 +07:00
claims [ "iat" ] = time . Now ( ) . Unix ( )
2022-03-16 05:55:38 +07:00
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." ) )
2021-07-10 11:56:33 +07:00
return
}
2022-03-16 05:55:38 +07:00
headers := & jwt . Headers {
Extra : map [ string ] interface { } { "kid" : keyID } ,
}
if token , _ , err = ctx . Providers . OpenIDConnect . KeyManager . Strategy ( ) . Generate ( req . Context ( ) , claims , headers ) ; err != nil {
2021-07-10 11:56:33 +07:00
ctx . Providers . OpenIDConnect . WriteError ( rw , req , err )
return
}
rw . Header ( ) . Set ( "Content-Type" , "application/jwt" )
_ , _ = rw . Write ( [ ] byte ( token ) )
case "none" , "" :
ctx . Providers . OpenIDConnect . Write ( rw , req , claims )
default :
2022-03-16 05:55:38 +07:00
ctx . Providers . OpenIDConnect . WriteError ( rw , req , errors . WithStack ( fosite . ErrServerError . WithHintf ( "Unsupported UserInfo signing algorithm '%s'." , client . UserinfoSigningAlgorithm ) ) )
2021-07-10 11:56:33 +07:00
}
}