diff --git a/dashboard/src/components/EnvComponents/JWTConfiguration.tsx b/dashboard/src/components/EnvComponents/JWTConfiguration.tsx index 86f4b39..0e4d2fc 100644 --- a/dashboard/src/components/EnvComponents/JWTConfiguration.tsx +++ b/dashboard/src/components/EnvComponents/JWTConfiguration.tsx @@ -61,7 +61,6 @@ const JSTConfigurations = ({ return (
- {' '} + + + Default Response Type: + + + + + + + + Default Response Mode: + + + + + diff --git a/dashboard/src/constants.ts b/dashboard/src/constants.ts index d9c8f3e..16f7cdb 100644 --- a/dashboard/src/constants.ts +++ b/dashboard/src/constants.ts @@ -57,6 +57,8 @@ export const ArrayInputType = { export const SelectInputType = { JWT_TYPE: 'JWT_TYPE', GENDER: 'gender', + DEFAULT_AUTHORIZE_RESPONSE_TYPE: 'DEFAULT_AUTHORIZE_RESPONSE_TYPE', + DEFAULT_AUTHORIZE_RESPONSE_MODE: 'DEFAULT_AUTHORIZE_RESPONSE_MODE', }; export const MultiSelectInputType = { @@ -161,6 +163,8 @@ export interface envVarTypes { ACCESS_TOKEN_EXPIRY_TIME: string; DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean; ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean; + DEFAULT_AUTHORIZE_RESPONSE_TYPE: string; + DEFAULT_AUTHORIZE_RESPONSE_MODE: string; } export const envSubViews = { @@ -349,3 +353,16 @@ export enum EmailTemplateEditors { UNLAYER_EDITOR = 'unlayer_editor', PLAIN_HTML_EDITOR = 'plain_html_editor', } + +export const ResponseTypes = { + token: 'token', + code: 'code', + id_token: 'id_token', +}; + +export const ResponseModes = { + query: 'query', + form_post: 'form_post', + fragment: 'fragment', + web_message: 'web_message', +}; diff --git a/dashboard/src/graphql/queries/index.ts b/dashboard/src/graphql/queries/index.ts index 5b3cdc3..5202023 100644 --- a/dashboard/src/graphql/queries/index.ts +++ b/dashboard/src/graphql/queries/index.ts @@ -70,6 +70,8 @@ export const EnvVariablesQuery = ` ACCESS_TOKEN_EXPIRY_TIME DISABLE_MULTI_FACTOR_AUTHENTICATION ENFORCE_MULTI_FACTOR_AUTHENTICATION + DEFAULT_AUTHORIZE_RESPONSE_TYPE + DEFAULT_AUTHORIZE_RESPONSE_MODE } } `; diff --git a/dashboard/src/pages/Environment.tsx b/dashboard/src/pages/Environment.tsx index 7c882d0..98bb8d7 100644 --- a/dashboard/src/pages/Environment.tsx +++ b/dashboard/src/pages/Environment.tsx @@ -90,6 +90,8 @@ const Environment = () => { ACCESS_TOKEN_EXPIRY_TIME: '', DISABLE_MULTI_FACTOR_AUTHENTICATION: false, ENFORCE_MULTI_FACTOR_AUTHENTICATION: false, + DEFAULT_AUTHORIZE_RESPONSE_TYPE: '', + DEFAULT_AUTHORIZE_RESPONSE_MODE: '', }); const [fieldVisibility, setFieldVisibility] = React.useState< diff --git a/server/constants/env.go b/server/constants/env.go index 970611e..8835c7f 100644 --- a/server/constants/env.go +++ b/server/constants/env.go @@ -166,4 +166,12 @@ const ( EnvKeyDefaultRoles = "DEFAULT_ROLES" // EnvKeyAllowedOrigins key for env variable ALLOWED_ORIGINS EnvKeyAllowedOrigins = "ALLOWED_ORIGINS" + + // For oauth/openid/authorize + // EnvKeyDefaultAuthorizeResponseType key for env variable DEFAULT_AUTHORIZE_RESPONSE_TYPE + // This env is used for setting default response type in authorize handler + EnvKeyDefaultAuthorizeResponseType = "DEFAULT_AUTHORIZE_RESPONSE_TYPE" + // EnvKeyDefaultAuthorizeResponseMode key for env variable DEFAULT_AUTHORIZE_RESPONSE_MODE + // This env is used for setting default response mode in authorize handler + EnvKeyDefaultAuthorizeResponseMode = "DEFAULT_AUTHORIZE_RESPONSE_MODE" ) diff --git a/server/env/env.go b/server/env/env.go index 90c5a50..1528a4c 100644 --- a/server/env/env.go +++ b/server/env/env.go @@ -87,6 +87,8 @@ func InitAllEnv() error { osCouchbaseBucket := os.Getenv(constants.EnvCouchbaseBucket) osCouchbaseScope := os.Getenv(constants.EnvCouchbaseScope) osCouchbaseBucketRAMQuotaMB := os.Getenv(constants.EnvCouchbaseBucketRAMQuotaMB) + osAuthorizeResponseType := os.Getenv(constants.EnvKeyDefaultAuthorizeResponseType) + osAuthorizeResponseMode := os.Getenv(constants.EnvKeyDefaultAuthorizeResponseMode) // os bool vars osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure) @@ -735,6 +737,28 @@ func InitAllEnv() error { envData[constants.EnvKeyProtectedRoles] = osProtectedRoles } + if val, ok := envData[constants.EnvKeyDefaultAuthorizeResponseType]; !ok || val == "" { + envData[constants.EnvKeyDefaultAuthorizeResponseType] = osAuthorizeResponseType + // Set the default value to token type + if envData[constants.EnvKeyDefaultAuthorizeResponseType] == "" { + envData[constants.EnvKeyDefaultAuthorizeResponseType] = constants.ResponseTypeToken + } + } + if osAuthorizeResponseType != "" && envData[constants.EnvKeyDefaultAuthorizeResponseType] != osAuthorizeResponseType { + envData[constants.EnvKeyDefaultAuthorizeResponseType] = osAuthorizeResponseType + } + + if val, ok := envData[constants.EnvKeyDefaultAuthorizeResponseMode]; !ok || val == "" { + envData[constants.EnvKeyDefaultAuthorizeResponseMode] = osAuthorizeResponseMode + // Set the default value to token type + if envData[constants.EnvKeyDefaultAuthorizeResponseMode] == "" { + envData[constants.EnvKeyDefaultAuthorizeResponseMode] = constants.ResponseModeQuery + } + } + if osAuthorizeResponseMode != "" && envData[constants.EnvKeyDefaultAuthorizeResponseMode] != osAuthorizeResponseMode { + envData[constants.EnvKeyDefaultAuthorizeResponseMode] = osAuthorizeResponseMode + } + err = memorystore.Provider.UpdateEnvStore(envData) if err != nil { log.Debug("Error while updating env store: ", err) diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go index a620c77..fb7ec75 100644 --- a/server/graph/generated/generated.go +++ b/server/graph/generated/generated.go @@ -88,6 +88,8 @@ type ComplexityRoot struct { DatabaseType func(childComplexity int) int DatabaseURL func(childComplexity int) int DatabaseUsername func(childComplexity int) int + DefaultAuthorizeResponseMode func(childComplexity int) int + DefaultAuthorizeResponseType func(childComplexity int) int DefaultRoles func(childComplexity int) int DisableBasicAuthentication func(childComplexity int) int DisableEmailVerification func(childComplexity int) int @@ -608,6 +610,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Env.DatabaseUsername(childComplexity), true + case "Env.DEFAULT_AUTHORIZE_RESPONSE_MODE": + if e.complexity.Env.DefaultAuthorizeResponseMode == nil { + break + } + + return e.complexity.Env.DefaultAuthorizeResponseMode(childComplexity), true + + case "Env.DEFAULT_AUTHORIZE_RESPONSE_TYPE": + if e.complexity.Env.DefaultAuthorizeResponseType == nil { + break + } + + return e.complexity.Env.DefaultAuthorizeResponseType(childComplexity), true + case "Env.DEFAULT_ROLES": if e.complexity.Env.DefaultRoles == nil { break @@ -2203,6 +2219,8 @@ type Env { ORGANIZATION_LOGO: String APP_COOKIE_SECURE: Boolean! ADMIN_COOKIE_SECURE: Boolean! + DEFAULT_AUTHORIZE_RESPONSE_TYPE: String + DEFAULT_AUTHORIZE_RESPONSE_MODE: String } type ValidateJWTTokenResponse { @@ -2317,6 +2335,8 @@ input UpdateEnvInput { MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: String ORGANIZATION_NAME: String ORGANIZATION_LOGO: String + DEFAULT_AUTHORIZE_RESPONSE_TYPE: String + DEFAULT_AUTHORIZE_RESPONSE_MODE: String } input AdminLoginInput { @@ -6424,6 +6444,88 @@ func (ec *executionContext) fieldContext_Env_ADMIN_COOKIE_SECURE(ctx context.Con return fc, nil } +func (ec *executionContext) _Env_DEFAULT_AUTHORIZE_RESPONSE_TYPE(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_TYPE(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DefaultAuthorizeResponseType, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_TYPE(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Env", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Env_DEFAULT_AUTHORIZE_RESPONSE_MODE(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_MODE(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DefaultAuthorizeResponseMode, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_MODE(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Env", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Error_message(ctx context.Context, field graphql.CollectedField, obj *model.Error) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Error_message(ctx, field) if err != nil { @@ -10104,6 +10206,10 @@ func (ec *executionContext) fieldContext_Query__env(ctx context.Context, field g return ec.fieldContext_Env_APP_COOKIE_SECURE(ctx, field) case "ADMIN_COOKIE_SECURE": return ec.fieldContext_Env_ADMIN_COOKIE_SECURE(ctx, field) + case "DEFAULT_AUTHORIZE_RESPONSE_TYPE": + return ec.fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_TYPE(ctx, field) + case "DEFAULT_AUTHORIZE_RESPONSE_MODE": + return ec.fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_MODE(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Env", field.Name) }, @@ -16016,7 +16122,7 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob asMap[k] = v } - fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SMTP_LOCAL_NAME", "SENDER_EMAIL", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "MICROSOFT_CLIENT_ID", "MICROSOFT_CLIENT_SECRET", "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID", "ORGANIZATION_NAME", "ORGANIZATION_LOGO"} + fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SMTP_LOCAL_NAME", "SENDER_EMAIL", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "MICROSOFT_CLIENT_ID", "MICROSOFT_CLIENT_SECRET", "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID", "ORGANIZATION_NAME", "ORGANIZATION_LOGO", "DEFAULT_AUTHORIZE_RESPONSE_TYPE", "DEFAULT_AUTHORIZE_RESPONSE_MODE"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -16415,6 +16521,22 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob if err != nil { return it, err } + case "DEFAULT_AUTHORIZE_RESPONSE_TYPE": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DEFAULT_AUTHORIZE_RESPONSE_TYPE")) + it.DefaultAuthorizeResponseType, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "DEFAULT_AUTHORIZE_RESPONSE_MODE": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DEFAULT_AUTHORIZE_RESPONSE_MODE")) + it.DefaultAuthorizeResponseMode, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } } } @@ -17329,6 +17451,14 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { invalids++ } + case "DEFAULT_AUTHORIZE_RESPONSE_TYPE": + + out.Values[i] = ec._Env_DEFAULT_AUTHORIZE_RESPONSE_TYPE(ctx, field, obj) + + case "DEFAULT_AUTHORIZE_RESPONSE_MODE": + + out.Values[i] = ec._Env_DEFAULT_AUTHORIZE_RESPONSE_MODE(ctx, field, obj) + default: panic("unknown field " + strconv.Quote(field.Name)) } diff --git a/server/graph/model/models_gen.go b/server/graph/model/models_gen.go index d2c7d23..9327306 100644 --- a/server/graph/model/models_gen.go +++ b/server/graph/model/models_gen.go @@ -117,6 +117,8 @@ type Env struct { OrganizationLogo *string `json:"ORGANIZATION_LOGO"` AppCookieSecure bool `json:"APP_COOKIE_SECURE"` AdminCookieSecure bool `json:"ADMIN_COOKIE_SECURE"` + DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE"` + DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE"` } type Error struct { @@ -353,6 +355,8 @@ type UpdateEnvInput struct { MicrosoftActiveDirectoryTenantID *string `json:"MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"` OrganizationName *string `json:"ORGANIZATION_NAME"` OrganizationLogo *string `json:"ORGANIZATION_LOGO"` + DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE"` + DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE"` } type UpdateProfileInput struct { diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls index aa95de1..abb9258 100644 --- a/server/graph/schema.graphqls +++ b/server/graph/schema.graphqls @@ -153,6 +153,8 @@ type Env { ORGANIZATION_LOGO: String APP_COOKIE_SECURE: Boolean! ADMIN_COOKIE_SECURE: Boolean! + DEFAULT_AUTHORIZE_RESPONSE_TYPE: String + DEFAULT_AUTHORIZE_RESPONSE_MODE: String } type ValidateJWTTokenResponse { @@ -267,6 +269,8 @@ input UpdateEnvInput { MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: String ORGANIZATION_NAME: String ORGANIZATION_LOGO: String + DEFAULT_AUTHORIZE_RESPONSE_TYPE: String + DEFAULT_AUTHORIZE_RESPONSE_MODE: String } input AdminLoginInput { diff --git a/server/handlers/authorize.go b/server/handlers/authorize.go index adb9d53..bad2363 100644 --- a/server/handlers/authorize.go +++ b/server/handlers/authorize.go @@ -83,7 +83,11 @@ func AuthorizeHandler() gin.HandlerFunc { } if responseMode == "" { - responseMode = constants.ResponseModeQuery + if val, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultAuthorizeResponseMode); err == nil { + responseType = val + } else { + responseType = constants.ResponseModeQuery + } } if redirectURI == "" { @@ -91,7 +95,11 @@ func AuthorizeHandler() gin.HandlerFunc { } if responseType == "" { - responseType = "token" + if val, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultAuthorizeResponseType); err == nil { + responseType = val + } else { + responseType = constants.ResponseTypeToken + } } if err := validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge); err != nil { diff --git a/server/resolvers/env.go b/server/resolvers/env.go index 1a68557..92226af 100644 --- a/server/resolvers/env.go +++ b/server/resolvers/env.go @@ -168,6 +168,12 @@ func EnvResolver(ctx context.Context) (*model.Env, error) { if val, ok := store[constants.EnvKeyOrganizationLogo]; ok { res.OrganizationLogo = refs.NewStringRef(val.(string)) } + if val, ok := store[constants.EnvKeyDefaultAuthorizeResponseType]; ok { + res.DefaultAuthorizeResponseType = refs.NewStringRef(val.(string)) + } + if val, ok := store[constants.EnvKeyDefaultAuthorizeResponseMode]; ok { + res.DefaultAuthorizeResponseMode = refs.NewStringRef(val.(string)) + } // string slice vars res.AllowedOrigins = strings.Split(store[constants.EnvKeyAllowedOrigins].(string), ",")