2020-06-05 05:50:04 +00:00
package oidc
import (
"context"
2020-10-16 05:49:38 +00:00
"strings"
2020-06-05 05:50:04 +00:00
"time"
2022-04-26 23:01:45 +00:00
"github.com/zitadel/logging"
"github.com/zitadel/oidc/v2/pkg/oidc"
"github.com/zitadel/oidc/v2/pkg/op"
2020-06-05 05:50:04 +00:00
2022-04-26 23:01:45 +00:00
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/http/middleware"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/user/model"
2020-06-05 05:50:04 +00:00
)
2020-10-21 08:18:34 +00:00
func ( o * OPStorage ) CreateAuthRequest ( ctx context . Context , req * oidc . AuthRequest , userID string ) ( _ op . AuthRequest , err error ) {
ctx , span := tracing . NewSpan ( ctx )
defer func ( ) { span . EndWithError ( err ) } ( )
2020-08-31 06:49:35 +00:00
userAgentID , ok := middleware . UserAgentIDFromCtx ( ctx )
2020-06-05 05:50:04 +00:00
if ! ok {
return nil , errors . ThrowPreconditionFailed ( nil , "OIDC-sd436" , "no user agent id" )
}
2021-12-17 15:11:18 +00:00
req . Scopes , err = o . assertProjectRoleScopes ( ctx , req . ClientID , req . Scopes )
2020-10-16 05:49:38 +00:00
if err != nil {
2021-12-17 15:11:18 +00:00
return nil , errors . ThrowPreconditionFailed ( err , "OIDC-Gqrfg" , "Errors.Internal" )
2020-10-16 05:49:38 +00:00
}
2020-06-05 05:50:04 +00:00
authRequest := CreateAuthRequestToBusiness ( ctx , req , userAgentID , userID )
resp , err := o . repo . CreateAuthRequest ( ctx , authRequest )
if err != nil {
return nil , err
}
return AuthRequestFromBusiness ( resp )
}
2020-10-21 08:18:34 +00:00
func ( o * OPStorage ) AuthRequestByID ( ctx context . Context , id string ) ( _ op . AuthRequest , err error ) {
ctx , span := tracing . NewSpan ( ctx )
defer func ( ) { span . EndWithError ( err ) } ( )
2020-08-31 06:49:35 +00:00
userAgentID , ok := middleware . UserAgentIDFromCtx ( ctx )
if ! ok {
return nil , errors . ThrowPreconditionFailed ( nil , "OIDC-D3g21" , "no user agent id" )
}
2022-04-05 05:58:09 +00:00
resp , err := o . repo . AuthRequestByIDCheckLoggedIn ( ctx , id , userAgentID )
2020-06-05 05:50:04 +00:00
if err != nil {
return nil , err
}
return AuthRequestFromBusiness ( resp )
}
2020-10-21 08:18:34 +00:00
func ( o * OPStorage ) AuthRequestByCode ( ctx context . Context , code string ) ( _ op . AuthRequest , err error ) {
ctx , span := tracing . NewSpan ( ctx )
defer func ( ) { span . EndWithError ( err ) } ( )
2022-03-23 08:02:39 +00:00
2022-04-05 05:58:09 +00:00
resp , err := o . repo . AuthRequestByCode ( ctx , code )
2020-06-05 05:50:04 +00:00
if err != nil {
return nil , err
}
return AuthRequestFromBusiness ( resp )
}
2020-10-21 08:18:34 +00:00
func ( o * OPStorage ) SaveAuthCode ( ctx context . Context , id , code string ) ( err error ) {
ctx , span := tracing . NewSpan ( ctx )
defer func ( ) { span . EndWithError ( err ) } ( )
2020-08-31 06:49:35 +00:00
userAgentID , ok := middleware . UserAgentIDFromCtx ( ctx )
if ! ok {
return errors . ThrowPreconditionFailed ( nil , "OIDC-Dgus2" , "no user agent id" )
}
2022-04-05 05:58:09 +00:00
return o . repo . SaveAuthCode ( ctx , id , code , userAgentID )
2020-06-05 05:50:04 +00:00
}
2020-10-21 08:18:34 +00:00
func ( o * OPStorage ) DeleteAuthRequest ( ctx context . Context , id string ) ( err error ) {
ctx , span := tracing . NewSpan ( ctx )
defer func ( ) { span . EndWithError ( err ) } ( )
2022-03-23 08:02:39 +00:00
2022-04-05 05:58:09 +00:00
return o . repo . DeleteAuthRequest ( ctx , id )
2020-06-05 05:50:04 +00:00
}
2021-05-20 11:33:35 +00:00
func ( o * OPStorage ) CreateAccessToken ( ctx context . Context , req op . TokenRequest ) ( _ string , _ time . Time , err error ) {
2020-10-21 08:18:34 +00:00
ctx , span := tracing . NewSpan ( ctx )
defer func ( ) { span . EndWithError ( err ) } ( )
2021-03-29 12:50:58 +00:00
var userAgentID , applicationID , userOrgID string
2020-09-17 06:49:33 +00:00
authReq , ok := req . ( * AuthRequest )
if ok {
userAgentID = authReq . AgentID
applicationID = authReq . ApplicationID
2021-03-29 12:50:58 +00:00
userOrgID = authReq . UserOrgID
2020-07-09 12:05:12 +00:00
}
2022-09-27 10:53:49 +00:00
accessTokenLifetime , _ , _ , _ , err := o . getOIDCSettings ( ctx )
if err != nil {
return "" , time . Time { } , err
}
resp , err := o . command . AddUserToken ( setContextUserSystem ( ctx ) , userOrgID , userAgentID , applicationID , req . GetSubject ( ) , req . GetAudience ( ) , req . GetScopes ( ) , accessTokenLifetime ) //PLANNED: lifetime from client
2020-06-05 05:50:04 +00:00
if err != nil {
return "" , time . Time { } , err
}
2020-10-15 11:52:41 +00:00
return resp . TokenID , resp . Expiration , nil
2020-06-05 05:50:04 +00:00
}
2021-05-20 11:33:35 +00:00
func ( o * OPStorage ) CreateAccessAndRefreshTokens ( ctx context . Context , req op . TokenRequest , refreshToken string ) ( _ , _ string , _ time . Time , err error ) {
ctx , span := tracing . NewSpan ( ctx )
defer func ( ) { span . EndWithError ( err ) } ( )
2021-05-26 07:01:07 +00:00
userAgentID , applicationID , userOrgID , authTime , authMethodsReferences := getInfoFromRequest ( req )
2021-12-17 15:11:18 +00:00
scopes , err := o . assertProjectRoleScopes ( ctx , applicationID , req . GetScopes ( ) )
if err != nil {
return "" , "" , time . Time { } , errors . ThrowPreconditionFailed ( err , "OIDC-Df2fq" , "Errors.Internal" )
}
if request , ok := req . ( op . RefreshTokenRequest ) ; ok {
request . SetCurrentScopes ( scopes )
}
2022-09-27 10:53:49 +00:00
accessTokenLifetime , _ , refreshTokenIdleExpiration , refreshTokenExpiration , err := o . getOIDCSettings ( ctx )
if err != nil {
return "" , "" , time . Time { } , err
}
2022-03-24 13:00:24 +00:00
resp , token , err := o . command . AddAccessAndRefreshToken ( setContextUserSystem ( ctx ) , userOrgID , userAgentID , applicationID , req . GetSubject ( ) ,
2022-09-27 10:53:49 +00:00
refreshToken , req . GetAudience ( ) , scopes , authMethodsReferences , accessTokenLifetime ,
refreshTokenIdleExpiration , refreshTokenExpiration , authTime ) //PLANNED: lifetime from client
2021-05-20 11:33:35 +00:00
if err != nil {
2021-11-03 07:35:24 +00:00
if errors . IsErrorInvalidArgument ( err ) {
err = oidc . ErrInvalidGrant ( ) . WithParent ( err )
}
2021-05-20 11:33:35 +00:00
return "" , "" , time . Time { } , err
}
return resp . TokenID , token , resp . Expiration , nil
}
2021-05-26 07:01:07 +00:00
func getInfoFromRequest ( req op . TokenRequest ) ( string , string , string , time . Time , [ ] string ) {
authReq , ok := req . ( * AuthRequest )
if ok {
return authReq . AgentID , authReq . ApplicationID , authReq . UserOrgID , authReq . AuthTime , authReq . GetAMR ( )
}
refreshReq , ok := req . ( * RefreshTokenRequest )
if ok {
return refreshReq . UserAgentID , refreshReq . ClientID , "" , refreshReq . AuthTime , refreshReq . AuthMethodsReferences
}
return "" , "" , "" , time . Time { } , nil
}
2021-05-20 11:33:35 +00:00
func ( o * OPStorage ) TokenRequestByRefreshToken ( ctx context . Context , refreshToken string ) ( op . RefreshTokenRequest , error ) {
tokenView , err := o . repo . RefreshTokenByID ( ctx , refreshToken )
if err != nil {
return nil , err
}
return RefreshTokenRequestFromBusiness ( tokenView ) , nil
}
2020-10-21 08:18:34 +00:00
func ( o * OPStorage ) TerminateSession ( ctx context . Context , userID , clientID string ) ( err error ) {
ctx , span := tracing . NewSpan ( ctx )
defer func ( ) { span . EndWithError ( err ) } ( )
2020-08-31 06:49:35 +00:00
userAgentID , ok := middleware . UserAgentIDFromCtx ( ctx )
2020-06-05 05:50:04 +00:00
if ! ok {
2022-07-27 07:49:16 +00:00
logging . Error ( "no user agent id" )
2020-06-05 05:50:04 +00:00
return errors . ThrowPreconditionFailed ( nil , "OIDC-fso7F" , "no user agent id" )
}
2021-02-08 10:30:30 +00:00
userIDs , err := o . repo . UserSessionUserIDsByAgentID ( ctx , userAgentID )
if err != nil {
2022-07-27 07:49:16 +00:00
logging . WithError ( err ) . Error ( "error retrieving user sessions" )
2021-02-08 10:30:30 +00:00
return err
}
2021-07-19 13:12:00 +00:00
if len ( userIDs ) == 0 {
return nil
}
2022-03-24 13:00:24 +00:00
data := authz . CtxData {
UserID : userID ,
}
err = o . command . HumansSignOut ( authz . SetCtxData ( ctx , data ) , userAgentID , userIDs )
2022-07-27 07:49:16 +00:00
logging . OnError ( err ) . Error ( "error signing out" )
2021-07-08 11:55:21 +00:00
return err
2020-06-05 05:50:04 +00:00
}
2021-11-03 07:35:24 +00:00
func ( o * OPStorage ) RevokeToken ( ctx context . Context , token , userID , clientID string ) * oidc . Error {
refreshToken , err := o . repo . RefreshTokenByID ( ctx , token )
if err == nil {
if refreshToken . ClientID != clientID {
return oidc . ErrInvalidClient ( ) . WithDescription ( "token was not issued for this client" )
}
_ , err = o . command . RevokeRefreshToken ( ctx , refreshToken . UserID , refreshToken . ResourceOwner , refreshToken . ID )
2021-11-03 13:10:01 +00:00
if err == nil || errors . IsNotFound ( err ) {
2021-11-03 07:35:24 +00:00
return nil
}
return oidc . ErrServerError ( ) . WithParent ( err )
}
2022-09-15 12:59:40 +00:00
accessToken , err := o . repo . TokenByIDs ( ctx , userID , token )
2021-11-03 07:35:24 +00:00
if err != nil {
if errors . IsNotFound ( err ) {
return nil
}
return oidc . ErrServerError ( ) . WithParent ( err )
}
if accessToken . ApplicationID != clientID {
return oidc . ErrInvalidClient ( ) . WithDescription ( "token was not issued for this client" )
}
_ , err = o . command . RevokeAccessToken ( ctx , userID , accessToken . ResourceOwner , accessToken . ID )
if err == nil || errors . IsNotFound ( err ) {
return nil
}
return oidc . ErrServerError ( ) . WithParent ( err )
}
2021-12-17 15:11:18 +00:00
func ( o * OPStorage ) assertProjectRoleScopes ( ctx context . Context , clientID string , scopes [ ] string ) ( [ ] string , error ) {
2020-10-16 05:49:38 +00:00
for _ , scope := range scopes {
if strings . HasPrefix ( scope , ScopeProjectRolePrefix ) {
return scopes , nil
}
}
2022-11-30 16:01:17 +00:00
projectID , err := o . query . ProjectIDFromOIDCClientID ( ctx , clientID , false )
2021-12-17 15:11:18 +00:00
if err != nil {
return nil , errors . ThrowPreconditionFailed ( nil , "OIDC-AEG4d" , "Errors.Internal" )
}
2022-11-30 16:01:17 +00:00
project , err := o . query . ProjectByID ( ctx , false , projectID , false )
2021-12-17 15:11:18 +00:00
if err != nil {
return nil , errors . ThrowPreconditionFailed ( nil , "OIDC-w4wIn" , "Errors.Internal" )
}
if ! project . ProjectRoleAssertion {
return scopes , nil
}
2021-11-26 06:57:05 +00:00
projectIDQuery , err := query . NewProjectRoleProjectIDSearchQuery ( project . ID )
2021-11-04 12:46:15 +00:00
if err != nil {
return nil , errors . ThrowInternal ( err , "OIDC-Cyc78" , "Errors.Internal" )
}
2022-11-30 16:01:17 +00:00
roles , err := o . query . SearchProjectRoles ( ctx , true , & query . ProjectRoleSearchQueries { Queries : [ ] query . SearchQuery { projectIDQuery } } , false )
2020-10-16 05:49:38 +00:00
if err != nil {
return nil , err
}
2021-11-04 12:46:15 +00:00
for _ , role := range roles . ProjectRoles {
2020-10-16 05:49:38 +00:00
scopes = append ( scopes , ScopeProjectRolePrefix + role . Key )
}
return scopes , nil
}
2022-02-08 08:37:28 +00:00
func ( o * OPStorage ) assertClientScopesForPAT ( ctx context . Context , token * model . TokenView , clientID string ) error {
token . Audience = append ( token . Audience , clientID )
2022-11-30 16:01:17 +00:00
projectID , err := o . query . ProjectIDFromClientID ( ctx , clientID , false )
2022-02-08 08:37:28 +00:00
if err != nil {
return errors . ThrowPreconditionFailed ( nil , "OIDC-AEG4d" , "Errors.Internal" )
}
projectIDQuery , err := query . NewProjectRoleProjectIDSearchQuery ( projectID )
if err != nil {
return errors . ThrowInternal ( err , "OIDC-Cyc78" , "Errors.Internal" )
}
2022-11-30 16:01:17 +00:00
roles , err := o . query . SearchProjectRoles ( ctx , true , & query . ProjectRoleSearchQueries { Queries : [ ] query . SearchQuery { projectIDQuery } } , false )
2022-02-08 08:37:28 +00:00
if err != nil {
return err
}
for _ , role := range roles . ProjectRoles {
token . Scopes = append ( token . Scopes , ScopeProjectRolePrefix + role . Key )
}
return nil
}
2022-03-24 13:00:24 +00:00
func setContextUserSystem ( ctx context . Context ) context . Context {
data := authz . CtxData {
UserID : "SYSTEM" ,
}
return authz . SetCtxData ( ctx , data )
}
2022-09-27 10:53:49 +00:00
func ( o * OPStorage ) getOIDCSettings ( ctx context . Context ) ( accessTokenLifetime , idTokenLifetime , refreshTokenIdleExpiration , refreshTokenExpiration time . Duration , _ error ) {
oidcSettings , err := o . query . OIDCSettingsByAggID ( ctx , authz . GetInstance ( ctx ) . InstanceID ( ) )
if err != nil && ! errors . IsNotFound ( err ) {
return time . Duration ( 0 ) , time . Duration ( 0 ) , time . Duration ( 0 ) , time . Duration ( 0 ) , err
}
if oidcSettings != nil {
return oidcSettings . AccessTokenLifetime , oidcSettings . IdTokenLifetime , oidcSettings . RefreshTokenIdleExpiration , oidcSettings . RefreshTokenExpiration , nil
}
return o . defaultAccessTokenLifetime , o . defaultIdTokenLifetime , o . defaultRefreshTokenIdleExpiration , o . defaultRefreshTokenExpiration , nil
}