Merge branch 'authorizerdev:main' into main

This commit is contained in:
Anand Kumar Panigrahi 2024-01-10 12:23:03 +05:30 committed by GitHub
commit a63d00b0c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2393 additions and 1630 deletions

16
app/package-lock.json generated
View File

@ -9,7 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^1.1.19", "@authorizerdev/authorizer-react": "^1.2.0",
"@types/react": "^17.0.15", "@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17", "esbuild": "^0.12.17",
@ -27,9 +27,9 @@
} }
}, },
"node_modules/@authorizerdev/authorizer-js": { "node_modules/@authorizerdev/authorizer-js": {
"version": "1.2.18", "version": "2.0.0-beta.3",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.2.18.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-2.0.0-beta.3.tgz",
"integrity": "sha512-9j5U/4lqaaEcG78Zli+TtLJ0migSKhFwnXXunulAGTZOzQSTCJ/CSSPip5wWNa/Mkr6gdEMwk1HYfhIdk2A9Mg==", "integrity": "sha512-cEzEVe7AewvOwOwoettiKRCq1e5Y33k9g8fJjqAoe3B/36iNN8wnZ5qgsPPZkqhv+Cvn6huj+YWtRimfVJ6d0w==",
"dependencies": { "dependencies": {
"cross-fetch": "^3.1.5" "cross-fetch": "^3.1.5"
}, },
@ -41,11 +41,11 @@
} }
}, },
"node_modules/@authorizerdev/authorizer-react": { "node_modules/@authorizerdev/authorizer-react": {
"version": "1.1.19", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.19.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.2.0.tgz",
"integrity": "sha512-hbId4mtzeWke1uUFAZrPwT45UmxgTp0QHAAsQvl/I0+mgoCJlJdAnUBCiJD6d5lVHJk41nx/ePYG4rw2Aj6HTw==", "integrity": "sha512-MtunZgh30rzY9jSADVP1DRC4sOBC82zx/yhK8O/1ufOAi7vTDZwPjDHIMrG/xWPNUYTCeFPEKpZlKyB+TH/M1w==",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-js": "^1.2.18", "@authorizerdev/authorizer-js": "^2.0.0-beta.3",
"validator": "^13.11.0" "validator": "^13.11.0"
}, },
"engines": { "engines": {

View File

@ -12,7 +12,7 @@
"author": "Lakhan Samani", "author": "Lakhan Samani",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^1.1.19", "@authorizerdev/authorizer-react": "^1.2.0",
"@types/react": "^17.0.15", "@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17", "esbuild": "^0.12.17",

View File

@ -27,13 +27,13 @@ export default function App() {
if (redirectURL) { if (redirectURL) {
urlProps.redirectURL = redirectURL; urlProps.redirectURL = redirectURL;
} else { } else {
urlProps.redirectURL = window.location.origin + '/app'; urlProps.redirectURL = window.location.href;
} }
const globalState: Record<string, string> = { const globalState: Record<string, string> = {
...window['__authorizer__'], ...window['__authorizer__'],
...urlProps, ...urlProps,
}; };
console.log({ globalState });
return ( return (
<div <div
style={{ style={{

View File

@ -59,7 +59,9 @@ export default function Root({
useEffect(() => { useEffect(() => {
if (token) { if (token) {
let redirectURL = config.redirectURL || '/app'; let redirectURL = config.redirectURL || '/app';
let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`; // let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
// Note: If OIDC breaks in the future, use the above params
let params = `state=${globalState.state}`;
if (code !== '') { if (code !== '') {
params += `&code=${code}`; params += `&code=${code}`;

View File

@ -71,6 +71,9 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
...urlProps, ...urlProps,
redirect_uri: `${window.location.origin}/app/reset-password`, redirect_uri: `${window.location.origin}/app/reset-password`,
}} }}
onPasswordReset={() => {
setView(VIEW_TYPES.LOGIN);
}}
/> />
<Footer> <Footer>
<Link <Link

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ import {
FaApple, FaApple,
FaTwitter, FaTwitter,
FaMicrosoft, FaMicrosoft,
FaTwitch, FaTwitch, FaDiscord,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { import {
TextInputType, TextInputType,
@ -309,6 +309,44 @@ const OAuthConfig = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaDiscord style={{ color: '#7289da' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.DISCORD_CLIENT_ID}
placeholder="Discord Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.DISCORD_CLIENT_SECRET}
placeholder="Discord Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center <Center
w={isNotSmallerScreen ? '55px' : '35px'} w={isNotSmallerScreen ? '55px' : '35px'}

View File

@ -9,6 +9,7 @@ export const TextInputType = {
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID', FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID', LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID', APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
DISCORD_CLIENT_ID: 'DISCORD_CLIENT_ID',
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID', TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID', MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID', MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID',
@ -41,6 +42,7 @@ export const HiddenInputType = {
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET', FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET', LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET', APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
DISCORD_CLIENT_SECRET: 'DISCORD_CLIENT_SECRET',
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET', TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET', MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET',
TWITCH_CLIENT_SECRET: 'TWITCH_CLIENT_SECRET', TWITCH_CLIENT_SECRET: 'TWITCH_CLIENT_SECRET',
@ -129,6 +131,8 @@ export interface envVarTypes {
LINKEDIN_CLIENT_SECRET: string; LINKEDIN_CLIENT_SECRET: string;
APPLE_CLIENT_ID: string; APPLE_CLIENT_ID: string;
APPLE_CLIENT_SECRET: string; APPLE_CLIENT_SECRET: string;
DISCORD_CLIENT_ID: string;
DISCORD_CLIENT_SECRET: string;
TWITTER_CLIENT_ID: string; TWITTER_CLIENT_ID: string;
TWITTER_CLIENT_SECRET: string; TWITTER_CLIENT_SECRET: string;
MICROSOFT_CLIENT_ID: string; MICROSOFT_CLIENT_ID: string;

View File

@ -30,6 +30,8 @@ export const EnvVariablesQuery = `
LINKEDIN_CLIENT_SECRET LINKEDIN_CLIENT_SECRET
APPLE_CLIENT_ID APPLE_CLIENT_ID
APPLE_CLIENT_SECRET APPLE_CLIENT_SECRET
DISCORD_CLIENT_ID
DISCORD_CLIENT_SECRET
TWITTER_CLIENT_ID TWITTER_CLIENT_ID
TWITTER_CLIENT_SECRET TWITTER_CLIENT_SECRET
MICROSOFT_CLIENT_ID MICROSOFT_CLIENT_ID

View File

@ -50,6 +50,8 @@ const Environment = () => {
LINKEDIN_CLIENT_SECRET: '', LINKEDIN_CLIENT_SECRET: '',
APPLE_CLIENT_ID: '', APPLE_CLIENT_ID: '',
APPLE_CLIENT_SECRET: '', APPLE_CLIENT_SECRET: '',
DISCORD_CLIENT_ID: '',
DISCORD_CLIENT_SECRET: '',
TWITTER_CLIENT_ID: '', TWITTER_CLIENT_ID: '',
TWITTER_CLIENT_SECRET: '', TWITTER_CLIENT_SECRET: '',
MICROSOFT_CLIENT_ID: '', MICROSOFT_CLIENT_ID: '',
@ -108,6 +110,7 @@ const Environment = () => {
FACEBOOK_CLIENT_SECRET: false, FACEBOOK_CLIENT_SECRET: false,
LINKEDIN_CLIENT_SECRET: false, LINKEDIN_CLIENT_SECRET: false,
APPLE_CLIENT_SECRET: false, APPLE_CLIENT_SECRET: false,
DISCORD_CLIENT_SECRET: false,
TWITTER_CLIENT_SECRET: false, TWITTER_CLIENT_SECRET: false,
TWITCH_CLIENT_SECRET: false, TWITCH_CLIENT_SECRET: false,
JWT_SECRET: false, JWT_SECRET: false,

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,8 @@ const (
AuthRecipeMethodLinkedIn = "linkedin" AuthRecipeMethodLinkedIn = "linkedin"
// AuthRecipeMethodApple is the apple auth method // AuthRecipeMethodApple is the apple auth method
AuthRecipeMethodApple = "apple" AuthRecipeMethodApple = "apple"
// AuthRecipeMethodDiscord is the discord auth method
AuthRecipeMethodDiscord = "discord"
// AuthRecipeMethodTwitter is the twitter auth method // AuthRecipeMethodTwitter is the twitter auth method
AuthRecipeMethodTwitter = "twitter" AuthRecipeMethodTwitter = "twitter"
// AuthRecipeMethodMicrosoft is the microsoft auth method // AuthRecipeMethodMicrosoft is the microsoft auth method

View File

@ -108,6 +108,10 @@ const (
EnvKeyAppleClientID = "APPLE_CLIENT_ID" EnvKeyAppleClientID = "APPLE_CLIENT_ID"
// EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET // EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET
EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET" EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET"
// EnvKeyDiscordClientID key for env variable DISCORD_CLIENT_ID
EnvKeyDiscordClientID = "DISCORD_CLIENT_ID"
// EnvKeyDiscordClientSecret key for env variable DISCORD_CLIENT_SECRET
EnvKeyDiscordClientSecret = "DISCORD_CLIENT_SECRET"
// EnvKeyTwitterClientID key for env variable TWITTER_CLIENT_ID // EnvKeyTwitterClientID key for env variable TWITTER_CLIENT_ID
EnvKeyTwitterClientID = "TWITTER_CLIENT_ID" EnvKeyTwitterClientID = "TWITTER_CLIENT_ID"
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET // EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET

View File

@ -17,6 +17,7 @@ const (
TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username" TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username"
DiscordUserInfoURL = "https://discord.com/api/oauth2/@me"
// Get microsoft user info. // Get microsoft user info.
// Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo // Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo
MicrosoftUserInfoURL = "https://graph.microsoft.com/oidc/userinfo" MicrosoftUserInfoURL = "https://graph.microsoft.com/oidc/userinfo"

View File

@ -109,6 +109,8 @@ type ComplexityRoot struct {
DisableSignUp func(childComplexity int) int DisableSignUp func(childComplexity int) int
DisableStrongPassword func(childComplexity int) int DisableStrongPassword func(childComplexity int) int
DisableTotpLogin func(childComplexity int) int DisableTotpLogin func(childComplexity int) int
DiscordClientID func(childComplexity int) int
DiscordClientSecret func(childComplexity int) int
EnforceMultiFactorAuthentication func(childComplexity int) int EnforceMultiFactorAuthentication func(childComplexity int) int
FacebookClientID func(childComplexity int) int FacebookClientID func(childComplexity int) int
FacebookClientSecret func(childComplexity int) int FacebookClientSecret func(childComplexity int) int
@ -150,6 +152,11 @@ type ComplexityRoot struct {
Reason func(childComplexity int) int Reason func(childComplexity int) int
} }
ForgotPasswordResponse struct {
Message func(childComplexity int) int
ShouldShowMobileOtpScreen func(childComplexity int) int
}
GenerateJWTKeysResponse struct { GenerateJWTKeysResponse struct {
PrivateKey func(childComplexity int) int PrivateKey func(childComplexity int) int
PublicKey func(childComplexity int) int PublicKey func(childComplexity int) int
@ -165,6 +172,7 @@ type ComplexityRoot struct {
ClientID func(childComplexity int) int ClientID func(childComplexity int) int
IsAppleLoginEnabled func(childComplexity int) int IsAppleLoginEnabled func(childComplexity int) int
IsBasicAuthenticationEnabled func(childComplexity int) int IsBasicAuthenticationEnabled func(childComplexity int) int
IsDiscordLoginEnabled func(childComplexity int) int
IsEmailVerificationEnabled func(childComplexity int) int IsEmailVerificationEnabled func(childComplexity int) int
IsFacebookLoginEnabled func(childComplexity int) int IsFacebookLoginEnabled func(childComplexity int) int
IsGithubLoginEnabled func(childComplexity int) int IsGithubLoginEnabled func(childComplexity int) int
@ -356,7 +364,7 @@ type MutationResolver interface {
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error) UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error) VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error)
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error)
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error) Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error)
VerifyOtp(ctx context.Context, params model.VerifyOTPRequest) (*model.AuthResponse, error) VerifyOtp(ctx context.Context, params model.VerifyOTPRequest) (*model.AuthResponse, error)
@ -787,6 +795,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.DisableTotpLogin(childComplexity), true return e.complexity.Env.DisableTotpLogin(childComplexity), true
case "Env.DISCORD_CLIENT_ID":
if e.complexity.Env.DiscordClientID == nil {
break
}
return e.complexity.Env.DiscordClientID(childComplexity), true
case "Env.DISCORD_CLIENT_SECRET":
if e.complexity.Env.DiscordClientSecret == nil {
break
}
return e.complexity.Env.DiscordClientSecret(childComplexity), true
case "Env.ENFORCE_MULTI_FACTOR_AUTHENTICATION": case "Env.ENFORCE_MULTI_FACTOR_AUTHENTICATION":
if e.complexity.Env.EnforceMultiFactorAuthentication == nil { if e.complexity.Env.EnforceMultiFactorAuthentication == nil {
break break
@ -1039,6 +1061,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Error.Reason(childComplexity), true return e.complexity.Error.Reason(childComplexity), true
case "ForgotPasswordResponse.message":
if e.complexity.ForgotPasswordResponse.Message == nil {
break
}
return e.complexity.ForgotPasswordResponse.Message(childComplexity), true
case "ForgotPasswordResponse.should_show_mobile_otp_screen":
if e.complexity.ForgotPasswordResponse.ShouldShowMobileOtpScreen == nil {
break
}
return e.complexity.ForgotPasswordResponse.ShouldShowMobileOtpScreen(childComplexity), true
case "GenerateJWTKeysResponse.private_key": case "GenerateJWTKeysResponse.private_key":
if e.complexity.GenerateJWTKeysResponse.PrivateKey == nil { if e.complexity.GenerateJWTKeysResponse.PrivateKey == nil {
break break
@ -1095,6 +1131,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Meta.IsBasicAuthenticationEnabled(childComplexity), true return e.complexity.Meta.IsBasicAuthenticationEnabled(childComplexity), true
case "Meta.is_discord_login_enabled":
if e.complexity.Meta.IsDiscordLoginEnabled == nil {
break
}
return e.complexity.Meta.IsDiscordLoginEnabled(childComplexity), true
case "Meta.is_email_verification_enabled": case "Meta.is_email_verification_enabled":
if e.complexity.Meta.IsEmailVerificationEnabled == nil { if e.complexity.Meta.IsEmailVerificationEnabled == nil {
break break
@ -2362,6 +2405,7 @@ type Meta {
is_github_login_enabled: Boolean! is_github_login_enabled: Boolean!
is_linkedin_login_enabled: Boolean! is_linkedin_login_enabled: Boolean!
is_apple_login_enabled: Boolean! is_apple_login_enabled: Boolean!
is_discord_login_enabled: Boolean!
is_twitter_login_enabled: Boolean! is_twitter_login_enabled: Boolean!
is_microsoft_login_enabled: Boolean! is_microsoft_login_enabled: Boolean!
is_twitch_login_enabled: Boolean! is_twitch_login_enabled: Boolean!
@ -2459,6 +2503,11 @@ type Response {
message: String! message: String!
} }
type ForgotPasswordResponse {
message: String!
should_show_mobile_otp_screen: Boolean
}
type InviteMembersResponse { type InviteMembersResponse {
message: String! message: String!
Users: [User!]! Users: [User!]!
@ -2515,6 +2564,8 @@ type Env {
LINKEDIN_CLIENT_SECRET: String LINKEDIN_CLIENT_SECRET: String
APPLE_CLIENT_ID: String APPLE_CLIENT_ID: String
APPLE_CLIENT_SECRET: String APPLE_CLIENT_SECRET: String
DISCORD_CLIENT_ID: String
DISCORD_CLIENT_SECRET: String
TWITTER_CLIENT_ID: String TWITTER_CLIENT_ID: String
TWITTER_CLIENT_SECRET: String TWITTER_CLIENT_SECRET: String
MICROSOFT_CLIENT_ID: String MICROSOFT_CLIENT_ID: String
@ -2644,6 +2695,8 @@ input UpdateEnvInput {
LINKEDIN_CLIENT_SECRET: String LINKEDIN_CLIENT_SECRET: String
APPLE_CLIENT_ID: String APPLE_CLIENT_ID: String
APPLE_CLIENT_SECRET: String APPLE_CLIENT_SECRET: String
DISCORD_CLIENT_ID: String
DISCORD_CLIENT_SECRET: String
TWITTER_CLIENT_ID: String TWITTER_CLIENT_ID: String
TWITTER_CLIENT_SECRET: String TWITTER_CLIENT_SECRET: String
MICROSOFT_CLIENT_ID: String MICROSOFT_CLIENT_ID: String
@ -2791,13 +2844,16 @@ input UpdateUserInput {
} }
input ForgotPasswordInput { input ForgotPasswordInput {
email: String! email: String
phone_number: String
state: String state: String
redirect_uri: String redirect_uri: String
} }
input ResetPasswordInput { input ResetPasswordInput {
token: String! token: String
otp: String
phone_number: String
password: String! password: String!
confirm_password: String! confirm_password: String!
} }
@ -2950,7 +3006,7 @@ type Mutation {
update_profile(params: UpdateProfileInput!): Response! update_profile(params: UpdateProfileInput!): Response!
verify_email(params: VerifyEmailInput!): AuthResponse! verify_email(params: VerifyEmailInput!): AuthResponse!
resend_verify_email(params: ResendVerifyEmailInput!): Response! resend_verify_email(params: ResendVerifyEmailInput!): Response!
forgot_password(params: ForgotPasswordInput!): Response! forgot_password(params: ForgotPasswordInput!): ForgotPasswordResponse!
reset_password(params: ResetPasswordInput!): Response! reset_password(params: ResetPasswordInput!): Response!
revoke(params: OAuthRevokeInput!): Response! revoke(params: OAuthRevokeInput!): Response!
verify_otp(params: VerifyOTPRequest!): AuthResponse! verify_otp(params: VerifyOTPRequest!): AuthResponse!
@ -6675,6 +6731,88 @@ func (ec *executionContext) fieldContext_Env_APPLE_CLIENT_SECRET(ctx context.Con
return fc, nil return fc, nil
} }
func (ec *executionContext) _Env_DISCORD_CLIENT_ID(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_DISCORD_CLIENT_ID(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.DiscordClientID, 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_DISCORD_CLIENT_ID(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_DISCORD_CLIENT_SECRET(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_DISCORD_CLIENT_SECRET(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.DiscordClientSecret, 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_DISCORD_CLIENT_SECRET(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_TWITTER_CLIENT_ID(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { func (ec *executionContext) _Env_TWITTER_CLIENT_ID(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_TWITTER_CLIENT_ID(ctx, field) fc, err := ec.fieldContext_Env_TWITTER_CLIENT_ID(ctx, field)
if err != nil { if err != nil {
@ -7434,6 +7572,91 @@ func (ec *executionContext) fieldContext_Error_reason(ctx context.Context, field
return fc, nil return fc, nil
} }
func (ec *executionContext) _ForgotPasswordResponse_message(ctx context.Context, field graphql.CollectedField, obj *model.ForgotPasswordResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ForgotPasswordResponse_message(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.Message, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ForgotPasswordResponse_message(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ForgotPasswordResponse",
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) _ForgotPasswordResponse_should_show_mobile_otp_screen(ctx context.Context, field graphql.CollectedField, obj *model.ForgotPasswordResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ForgotPasswordResponse_should_show_mobile_otp_screen(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.ShouldShowMobileOtpScreen, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*bool)
fc.Result = res
return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ForgotPasswordResponse_should_show_mobile_otp_screen(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ForgotPasswordResponse",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _GenerateJWTKeysResponse_secret(ctx context.Context, field graphql.CollectedField, obj *model.GenerateJWTKeysResponse) (ret graphql.Marshaler) { func (ec *executionContext) _GenerateJWTKeysResponse_secret(ctx context.Context, field graphql.CollectedField, obj *model.GenerateJWTKeysResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_GenerateJWTKeysResponse_secret(ctx, field) fc, err := ec.fieldContext_GenerateJWTKeysResponse_secret(ctx, field)
if err != nil { if err != nil {
@ -7995,6 +8218,50 @@ func (ec *executionContext) fieldContext_Meta_is_apple_login_enabled(ctx context
return fc, nil return fc, nil
} }
func (ec *executionContext) _Meta_is_discord_login_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Meta_is_discord_login_enabled(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.IsDiscordLoginEnabled, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Meta_is_discord_login_enabled(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Meta",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Meta_is_twitter_login_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) { func (ec *executionContext) _Meta_is_twitter_login_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Meta_is_twitter_login_enabled(ctx, field) fc, err := ec.fieldContext_Meta_is_twitter_login_enabled(ctx, field)
if err != nil { if err != nil {
@ -9135,9 +9402,9 @@ func (ec *executionContext) _Mutation_forgot_password(ctx context.Context, field
} }
return graphql.Null return graphql.Null
} }
res := resTmp.(*model.Response) res := resTmp.(*model.ForgotPasswordResponse)
fc.Result = res fc.Result = res
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res) return ec.marshalNForgotPasswordResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐForgotPasswordResponse(ctx, field.Selections, res)
} }
func (ec *executionContext) fieldContext_Mutation_forgot_password(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { func (ec *executionContext) fieldContext_Mutation_forgot_password(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@ -9149,9 +9416,11 @@ func (ec *executionContext) fieldContext_Mutation_forgot_password(ctx context.Co
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name { switch field.Name {
case "message": case "message":
return ec.fieldContext_Response_message(ctx, field) return ec.fieldContext_ForgotPasswordResponse_message(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_ForgotPasswordResponse_should_show_mobile_otp_screen(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type Response", field.Name) return nil, fmt.Errorf("no field named %q was found under type ForgotPasswordResponse", field.Name)
}, },
} }
defer func() { defer func() {
@ -10741,6 +11010,8 @@ func (ec *executionContext) fieldContext_Query_meta(ctx context.Context, field g
return ec.fieldContext_Meta_is_linkedin_login_enabled(ctx, field) return ec.fieldContext_Meta_is_linkedin_login_enabled(ctx, field)
case "is_apple_login_enabled": case "is_apple_login_enabled":
return ec.fieldContext_Meta_is_apple_login_enabled(ctx, field) return ec.fieldContext_Meta_is_apple_login_enabled(ctx, field)
case "is_discord_login_enabled":
return ec.fieldContext_Meta_is_discord_login_enabled(ctx, field)
case "is_twitter_login_enabled": case "is_twitter_login_enabled":
return ec.fieldContext_Meta_is_twitter_login_enabled(ctx, field) return ec.fieldContext_Meta_is_twitter_login_enabled(ctx, field)
case "is_microsoft_login_enabled": case "is_microsoft_login_enabled":
@ -11465,6 +11736,10 @@ func (ec *executionContext) fieldContext_Query__env(ctx context.Context, field g
return ec.fieldContext_Env_APPLE_CLIENT_ID(ctx, field) return ec.fieldContext_Env_APPLE_CLIENT_ID(ctx, field)
case "APPLE_CLIENT_SECRET": case "APPLE_CLIENT_SECRET":
return ec.fieldContext_Env_APPLE_CLIENT_SECRET(ctx, field) return ec.fieldContext_Env_APPLE_CLIENT_SECRET(ctx, field)
case "DISCORD_CLIENT_ID":
return ec.fieldContext_Env_DISCORD_CLIENT_ID(ctx, field)
case "DISCORD_CLIENT_SECRET":
return ec.fieldContext_Env_DISCORD_CLIENT_SECRET(ctx, field)
case "TWITTER_CLIENT_ID": case "TWITTER_CLIENT_ID":
return ec.fieldContext_Env_TWITTER_CLIENT_ID(ctx, field) return ec.fieldContext_Env_TWITTER_CLIENT_ID(ctx, field)
case "TWITTER_CLIENT_SECRET": case "TWITTER_CLIENT_SECRET":
@ -16821,7 +17096,7 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"email", "state", "redirect_uri"} fieldsInOrder := [...]string{"email", "phone_number", "state", "redirect_uri"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@ -16832,11 +17107,20 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
var err error var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalNString2string(ctx, v) data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
it.Email = data it.Email = data
case "phone_number":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("phone_number"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.PhoneNumber = data
case "state": case "state":
var err error var err error
@ -17578,7 +17862,7 @@ func (ec *executionContext) unmarshalInputResetPasswordInput(ctx context.Context
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"token", "password", "confirm_password"} fieldsInOrder := [...]string{"token", "otp", "phone_number", "password", "confirm_password"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@ -17589,11 +17873,29 @@ func (ec *executionContext) unmarshalInputResetPasswordInput(ctx context.Context
var err error var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("token")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("token"))
data, err := ec.unmarshalNString2string(ctx, v) data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
it.Token = data it.Token = data
case "otp":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("otp"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.Otp = data
case "phone_number":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("phone_number"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.PhoneNumber = data
case "password": case "password":
var err error var err error
@ -17986,7 +18288,7 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
asMap[k] = v 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", "SENDER_NAME", "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", "TWITCH_CLIENT_ID", "TWITCH_CLIENT_SECRET", "ORGANIZATION_NAME", "ORGANIZATION_LOGO", "DEFAULT_AUTHORIZE_RESPONSE_TYPE", "DEFAULT_AUTHORIZE_RESPONSE_MODE", "DISABLE_PLAYGROUND", "DISABLE_MAIL_OTP_LOGIN", "DISABLE_TOTP_LOGIN"} 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", "SENDER_NAME", "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", "DISCORD_CLIENT_ID", "DISCORD_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "MICROSOFT_CLIENT_ID", "MICROSOFT_CLIENT_SECRET", "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID", "TWITCH_CLIENT_ID", "TWITCH_CLIENT_SECRET", "ORGANIZATION_NAME", "ORGANIZATION_LOGO", "DEFAULT_AUTHORIZE_RESPONSE_TYPE", "DEFAULT_AUTHORIZE_RESPONSE_MODE", "DISABLE_PLAYGROUND", "DISABLE_MAIL_OTP_LOGIN", "DISABLE_TOTP_LOGIN"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@ -18380,6 +18682,24 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
return it, err return it, err
} }
it.AppleClientSecret = data it.AppleClientSecret = data
case "DISCORD_CLIENT_ID":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DISCORD_CLIENT_ID"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.DiscordClientID = data
case "DISCORD_CLIENT_SECRET":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DISCORD_CLIENT_SECRET"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.DiscordClientSecret = data
case "TWITTER_CLIENT_ID": case "TWITTER_CLIENT_ID":
var err error var err error
@ -19415,6 +19735,10 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._Env_APPLE_CLIENT_ID(ctx, field, obj) out.Values[i] = ec._Env_APPLE_CLIENT_ID(ctx, field, obj)
case "APPLE_CLIENT_SECRET": case "APPLE_CLIENT_SECRET":
out.Values[i] = ec._Env_APPLE_CLIENT_SECRET(ctx, field, obj) out.Values[i] = ec._Env_APPLE_CLIENT_SECRET(ctx, field, obj)
case "DISCORD_CLIENT_ID":
out.Values[i] = ec._Env_DISCORD_CLIENT_ID(ctx, field, obj)
case "DISCORD_CLIENT_SECRET":
out.Values[i] = ec._Env_DISCORD_CLIENT_SECRET(ctx, field, obj)
case "TWITTER_CLIENT_ID": case "TWITTER_CLIENT_ID":
out.Values[i] = ec._Env_TWITTER_CLIENT_ID(ctx, field, obj) out.Values[i] = ec._Env_TWITTER_CLIENT_ID(ctx, field, obj)
case "TWITTER_CLIENT_SECRET": case "TWITTER_CLIENT_SECRET":
@ -19529,6 +19853,47 @@ func (ec *executionContext) _Error(ctx context.Context, sel ast.SelectionSet, ob
return out return out
} }
var forgotPasswordResponseImplementors = []string{"ForgotPasswordResponse"}
func (ec *executionContext) _ForgotPasswordResponse(ctx context.Context, sel ast.SelectionSet, obj *model.ForgotPasswordResponse) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, forgotPasswordResponseImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("ForgotPasswordResponse")
case "message":
out.Values[i] = ec._ForgotPasswordResponse_message(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "should_show_mobile_otp_screen":
out.Values[i] = ec._ForgotPasswordResponse_should_show_mobile_otp_screen(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.processDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var generateJWTKeysResponseImplementors = []string{"GenerateJWTKeysResponse"} var generateJWTKeysResponseImplementors = []string{"GenerateJWTKeysResponse"}
func (ec *executionContext) _GenerateJWTKeysResponse(ctx context.Context, sel ast.SelectionSet, obj *model.GenerateJWTKeysResponse) graphql.Marshaler { func (ec *executionContext) _GenerateJWTKeysResponse(ctx context.Context, sel ast.SelectionSet, obj *model.GenerateJWTKeysResponse) graphql.Marshaler {
@ -19659,6 +20024,11 @@ func (ec *executionContext) _Meta(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ out.Invalids++
} }
case "is_discord_login_enabled":
out.Values[i] = ec._Meta_is_discord_login_enabled(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "is_twitter_login_enabled": case "is_twitter_login_enabled":
out.Values[i] = ec._Meta_is_twitter_login_enabled(ctx, field, obj) out.Values[i] = ec._Meta_is_twitter_login_enabled(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@ -21531,6 +21901,20 @@ func (ec *executionContext) unmarshalNForgotPasswordInput2githubᚗcomᚋauthori
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
} }
func (ec *executionContext) marshalNForgotPasswordResponse2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐForgotPasswordResponse(ctx context.Context, sel ast.SelectionSet, v model.ForgotPasswordResponse) graphql.Marshaler {
return ec._ForgotPasswordResponse(ctx, sel, &v)
}
func (ec *executionContext) marshalNForgotPasswordResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐForgotPasswordResponse(ctx context.Context, sel ast.SelectionSet, v *model.ForgotPasswordResponse) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._ForgotPasswordResponse(ctx, sel, v)
}
func (ec *executionContext) unmarshalNGenerateJWTKeysInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐGenerateJWTKeysInput(ctx context.Context, v interface{}) (model.GenerateJWTKeysInput, error) { func (ec *executionContext) unmarshalNGenerateJWTKeysInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐGenerateJWTKeysInput(ctx context.Context, v interface{}) (model.GenerateJWTKeysInput, error) {
res, err := ec.unmarshalInputGenerateJWTKeysInput(ctx, v) res, err := ec.unmarshalInputGenerateJWTKeysInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)

View File

@ -114,6 +114,8 @@ type Env struct {
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET,omitempty"` LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET,omitempty"`
AppleClientID *string `json:"APPLE_CLIENT_ID,omitempty"` AppleClientID *string `json:"APPLE_CLIENT_ID,omitempty"`
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET,omitempty"` AppleClientSecret *string `json:"APPLE_CLIENT_SECRET,omitempty"`
DiscordClientID *string `json:"DISCORD_CLIENT_ID,omitempty"`
DiscordClientSecret *string `json:"DISCORD_CLIENT_SECRET,omitempty"`
TwitterClientID *string `json:"TWITTER_CLIENT_ID,omitempty"` TwitterClientID *string `json:"TWITTER_CLIENT_ID,omitempty"`
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET,omitempty"` TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET,omitempty"`
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID,omitempty"` MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID,omitempty"`
@ -138,11 +140,17 @@ type Error struct {
} }
type ForgotPasswordInput struct { type ForgotPasswordInput struct {
Email string `json:"email"` Email *string `json:"email,omitempty"`
PhoneNumber *string `json:"phone_number,omitempty"`
State *string `json:"state,omitempty"` State *string `json:"state,omitempty"`
RedirectURI *string `json:"redirect_uri,omitempty"` RedirectURI *string `json:"redirect_uri,omitempty"`
} }
type ForgotPasswordResponse struct {
Message string `json:"message"`
ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_otp_screen,omitempty"`
}
type GenerateJWTKeysInput struct { type GenerateJWTKeysInput struct {
Type string `json:"type"` Type string `json:"type"`
} }
@ -198,6 +206,7 @@ type Meta struct {
IsGithubLoginEnabled bool `json:"is_github_login_enabled"` IsGithubLoginEnabled bool `json:"is_github_login_enabled"`
IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"` IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"`
IsAppleLoginEnabled bool `json:"is_apple_login_enabled"` IsAppleLoginEnabled bool `json:"is_apple_login_enabled"`
IsDiscordLoginEnabled bool `json:"is_discord_login_enabled"`
IsTwitterLoginEnabled bool `json:"is_twitter_login_enabled"` IsTwitterLoginEnabled bool `json:"is_twitter_login_enabled"`
IsMicrosoftLoginEnabled bool `json:"is_microsoft_login_enabled"` IsMicrosoftLoginEnabled bool `json:"is_microsoft_login_enabled"`
IsTwitchLoginEnabled bool `json:"is_twitch_login_enabled"` IsTwitchLoginEnabled bool `json:"is_twitch_login_enabled"`
@ -272,9 +281,11 @@ type ResendVerifyEmailInput struct {
} }
type ResetPasswordInput struct { type ResetPasswordInput struct {
Token string `json:"token"` Token *string `json:"token,omitempty"`
Password string `json:"password"` Otp *string `json:"otp,omitempty"`
ConfirmPassword string `json:"confirm_password"` PhoneNumber *string `json:"phone_number,omitempty"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
} }
type Response struct { type Response struct {
@ -383,6 +394,8 @@ type UpdateEnvInput struct {
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET,omitempty"` LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET,omitempty"`
AppleClientID *string `json:"APPLE_CLIENT_ID,omitempty"` AppleClientID *string `json:"APPLE_CLIENT_ID,omitempty"`
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET,omitempty"` AppleClientSecret *string `json:"APPLE_CLIENT_SECRET,omitempty"`
DiscordClientID *string `json:"DISCORD_CLIENT_ID,omitempty"`
DiscordClientSecret *string `json:"DISCORD_CLIENT_SECRET,omitempty"`
TwitterClientID *string `json:"TWITTER_CLIENT_ID,omitempty"` TwitterClientID *string `json:"TWITTER_CLIENT_ID,omitempty"`
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET,omitempty"` TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET,omitempty"`
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID,omitempty"` MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID,omitempty"`

View File

@ -20,6 +20,7 @@ type Meta {
is_github_login_enabled: Boolean! is_github_login_enabled: Boolean!
is_linkedin_login_enabled: Boolean! is_linkedin_login_enabled: Boolean!
is_apple_login_enabled: Boolean! is_apple_login_enabled: Boolean!
is_discord_login_enabled: Boolean!
is_twitter_login_enabled: Boolean! is_twitter_login_enabled: Boolean!
is_microsoft_login_enabled: Boolean! is_microsoft_login_enabled: Boolean!
is_twitch_login_enabled: Boolean! is_twitch_login_enabled: Boolean!
@ -117,6 +118,11 @@ type Response {
message: String! message: String!
} }
type ForgotPasswordResponse {
message: String!
should_show_mobile_otp_screen: Boolean
}
type InviteMembersResponse { type InviteMembersResponse {
message: String! message: String!
Users: [User!]! Users: [User!]!
@ -173,6 +179,8 @@ type Env {
LINKEDIN_CLIENT_SECRET: String LINKEDIN_CLIENT_SECRET: String
APPLE_CLIENT_ID: String APPLE_CLIENT_ID: String
APPLE_CLIENT_SECRET: String APPLE_CLIENT_SECRET: String
DISCORD_CLIENT_ID: String
DISCORD_CLIENT_SECRET: String
TWITTER_CLIENT_ID: String TWITTER_CLIENT_ID: String
TWITTER_CLIENT_SECRET: String TWITTER_CLIENT_SECRET: String
MICROSOFT_CLIENT_ID: String MICROSOFT_CLIENT_ID: String
@ -302,6 +310,8 @@ input UpdateEnvInput {
LINKEDIN_CLIENT_SECRET: String LINKEDIN_CLIENT_SECRET: String
APPLE_CLIENT_ID: String APPLE_CLIENT_ID: String
APPLE_CLIENT_SECRET: String APPLE_CLIENT_SECRET: String
DISCORD_CLIENT_ID: String
DISCORD_CLIENT_SECRET: String
TWITTER_CLIENT_ID: String TWITTER_CLIENT_ID: String
TWITTER_CLIENT_SECRET: String TWITTER_CLIENT_SECRET: String
MICROSOFT_CLIENT_ID: String MICROSOFT_CLIENT_ID: String
@ -449,13 +459,16 @@ input UpdateUserInput {
} }
input ForgotPasswordInput { input ForgotPasswordInput {
email: String! email: String
phone_number: String
state: String state: String
redirect_uri: String redirect_uri: String
} }
input ResetPasswordInput { input ResetPasswordInput {
token: String! token: String
otp: String
phone_number: String
password: String! password: String!
confirm_password: String! confirm_password: String!
} }
@ -608,7 +621,7 @@ type Mutation {
update_profile(params: UpdateProfileInput!): Response! update_profile(params: UpdateProfileInput!): Response!
verify_email(params: VerifyEmailInput!): AuthResponse! verify_email(params: VerifyEmailInput!): AuthResponse!
resend_verify_email(params: ResendVerifyEmailInput!): Response! resend_verify_email(params: ResendVerifyEmailInput!): Response!
forgot_password(params: ForgotPasswordInput!): Response! forgot_password(params: ForgotPasswordInput!): ForgotPasswordResponse!
reset_password(params: ResetPasswordInput!): Response! reset_password(params: ResetPasswordInput!): Response!
revoke(params: OAuthRevokeInput!): Response! revoke(params: OAuthRevokeInput!): Response!
verify_otp(params: VerifyOTPRequest!): AuthResponse! verify_otp(params: VerifyOTPRequest!): AuthResponse!

View File

@ -58,7 +58,7 @@ func (r *mutationResolver) ResendVerifyEmail(ctx context.Context, params model.R
} }
// ForgotPassword is the resolver for the forgot_password field. // ForgotPassword is the resolver for the forgot_password field.
func (r *mutationResolver) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) { func (r *mutationResolver) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error) {
return resolvers.ForgotPasswordResolver(ctx, params) return resolvers.ForgotPasswordResolver(ctx, params)
} }

View File

@ -123,7 +123,7 @@ func AuthorizeHandler() gin.HandlerFunc {
// TODO add state with timeout // TODO add state with timeout
// used for response mode query or fragment // used for response mode query or fragment
authState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI authState := "state=" + state + "&scope=" + scopeString + "&redirect_uri=" + redirectURI
if responseType == constants.ResponseTypeCode { if responseType == constants.ResponseTypeCode {
authState += "&code=" + code authState += "&code=" + code
if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil { if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil {

View File

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strconv"
"strings" "strings"
"time" "time"
@ -54,7 +53,16 @@ func OAuthCallbackHandler() gin.HandlerFunc {
stateValue := sessionSplit[0] stateValue := sessionSplit[0]
redirectURL := sessionSplit[1] redirectURL := sessionSplit[1]
inputRoles := strings.Split(sessionSplit[2], ",") inputRoles := strings.Split(sessionSplit[2], ",")
scopes := strings.Split(sessionSplit[3], ",") scopeString := sessionSplit[3]
scopes := []string{}
if scopeString != "" {
if strings.Contains(scopeString, ",") {
scopes = strings.Split(scopeString, ",")
}
if strings.Contains(scopeString, " ") {
scopes = strings.Split(scopeString, " ")
}
}
var user *models.User var user *models.User
oauthCode := ctx.Request.FormValue("code") oauthCode := ctx.Request.FormValue("code")
if oauthCode == "" { if oauthCode == "" {
@ -73,6 +81,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user, err = processLinkedInUserInfo(ctx, oauthCode) user, err = processLinkedInUserInfo(ctx, oauthCode)
case constants.AuthRecipeMethodApple: case constants.AuthRecipeMethodApple:
user, err = processAppleUserInfo(ctx, oauthCode) user, err = processAppleUserInfo(ctx, oauthCode)
case constants.AuthRecipeMethodDiscord:
user, err = processDiscordUserInfo(ctx, oauthCode)
case constants.AuthRecipeMethodTwitter: case constants.AuthRecipeMethodTwitter:
user, err = processTwitterUserInfo(ctx, oauthCode, sessionState) user, err = processTwitterUserInfo(ctx, oauthCode, sessionState)
case constants.AuthRecipeMethodMicrosoft: case constants.AuthRecipeMethodMicrosoft:
@ -248,8 +258,9 @@ func OAuthCallbackHandler() gin.HandlerFunc {
expiresIn = 1 expiresIn = 1
} }
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token + "&nonce=" + nonce // params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token + "&nonce=" + nonce
// Note: If OIDC breaks in the future, use the above params
params := "state=" + stateValue + "&nonce=" + nonce
if code != "" { if code != "" {
params += "&code=" + code params += "&code=" + code
} }
@ -609,6 +620,71 @@ func processAppleUserInfo(ctx context.Context, code string) (*models.User, error
return user, err return user, err
} }
func processDiscordUserInfo(ctx context.Context, code string) (*models.User, error) {
oauth2Token, err := oauth.OAuthProviders.DiscordConfig.Exchange(ctx, code)
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return nil, fmt.Errorf("invalid discord exchange code: %s", err.Error())
}
client := http.Client{}
req, err := http.NewRequest("GET", constants.DiscordUserInfoURL, nil)
if err != nil {
log.Debug("Failed to create Discord user info request: ", err)
return nil, fmt.Errorf("error creating Discord user info request: %s", err.Error())
}
req.Header = http.Header{
"Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)},
}
response, err := client.Do(req)
if err != nil {
log.Debug("Failed to request Discord user info: ", err)
return nil, err
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read Discord user info response body: ", err)
return nil, fmt.Errorf("failed to read Discord response body: %s", err.Error())
}
if response.StatusCode >= 400 {
log.Debug("Failed to request Discord user info: ", string(body))
return nil, fmt.Errorf("failed to request Discord user info: %s", string(body))
}
// Unmarshal the response body into a map
responseRawData := make(map[string]interface{})
if err := json.Unmarshal(body, &responseRawData); err != nil {
log.Debug("Failed to unmarshal Discord response: ", err)
return nil, fmt.Errorf("failed to unmarshal Discord response: %s", err.Error())
}
// Safely extract the user data
userRawData, ok := responseRawData["user"].(map[string]interface{})
if !ok {
log.Debug("User data is not in expected format or missing in response")
return nil, fmt.Errorf("user data is not in expected format or missing in response")
}
// Extract the username
firstName, ok := userRawData["username"].(string)
if !ok {
log.Debug("Username is not in expected format or missing in user data")
return nil, fmt.Errorf("username is not in expected format or missing in user data")
}
profilePicture := fmt.Sprintf("https://cdn.discordapp.com/avatars/%s/%s.png", userRawData["id"].(string), userRawData["avatar"].(string))
user := &models.User{
GivenName: &firstName,
Picture: &profilePicture,
}
return user, nil
}
func processTwitterUserInfo(ctx context.Context, code, verifier string) (*models.User, error) { func processTwitterUserInfo(ctx context.Context, code, verifier string) (*models.User, error) {
oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(ctx, code, oauth2.SetAuthURLParam("code_verifier", verifier)) oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(ctx, code, oauth2.SetAuthURLParam("code_verifier", verifier))
if err != nil { if err != nil {

View File

@ -192,6 +192,24 @@ func OAuthLoginHandler() gin.HandlerFunc {
oauth.OAuthProviders.TwitterConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodTwitter oauth.OAuthProviders.TwitterConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodTwitter
url := oauth.OAuthProviders.TwitterConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("code_challenge", challenge), oauth2.SetAuthURLParam("code_challenge_method", "S256")) url := oauth.OAuthProviders.TwitterConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("code_challenge", challenge), oauth2.SetAuthURLParam("code_challenge_method", "S256"))
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
case constants.AuthRecipeMethodDiscord:
if oauth.OAuthProviders.DiscordConfig == nil {
log.Debug("Discord OAuth provider is not configured")
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodDiscord)
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
oauth.OAuthProviders.DiscordConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodDiscord
url := oauth.OAuthProviders.DiscordConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.AuthRecipeMethodApple: case constants.AuthRecipeMethodApple:
if oauth.OAuthProviders.AppleConfig == nil { if oauth.OAuthProviders.AppleConfig == nil {
log.Debug("Apple OAuth provider is not configured") log.Debug("Apple OAuth provider is not configured")

View File

@ -105,7 +105,7 @@ func TokenHandler() gin.HandlerFunc {
if codeVerifier == "" && clientSecret == "" { if codeVerifier == "" && clientSecret == "" {
gc.JSON(http.StatusBadRequest, gin.H{ gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_dat", "error": "invalid_data",
"error_description": "The code verifier or client secret is required", "error_description": "The code verifier or client secret is required",
}) })
return return
@ -263,12 +263,10 @@ func TokenHandler() gin.HandlerFunc {
"roles": roles, "roles": roles,
"expires_in": expiresIn, "expires_in": expiresIn,
} }
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res["refresh_token"] = authToken.RefreshToken.Token res["refresh_token"] = authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
gc.JSON(http.StatusOK, res) gc.JSON(http.StatusOK, res)
} }
} }

View File

@ -30,6 +30,7 @@ type OAuthProvider struct {
FacebookConfig *oauth2.Config FacebookConfig *oauth2.Config
LinkedInConfig *oauth2.Config LinkedInConfig *oauth2.Config
AppleConfig *oauth2.Config AppleConfig *oauth2.Config
DiscordConfig *oauth2.Config
TwitterConfig *oauth2.Config TwitterConfig *oauth2.Config
MicrosoftConfig *oauth2.Config MicrosoftConfig *oauth2.Config
TwitchConfig *oauth2.Config TwitchConfig *oauth2.Config
@ -149,6 +150,27 @@ func InitOAuth() error {
} }
} }
discordClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDiscordClientID)
if err != nil {
discordClientID = ""
}
discordClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDiscordClientSecret)
if err != nil {
discordClientSecret = ""
}
if discordClientID != "" && discordClientSecret != "" {
OAuthProviders.DiscordConfig = &oauth2.Config{
ClientID: discordClientID,
ClientSecret: discordClientSecret,
RedirectURL: "/oauth_callback/discord",
Endpoint: oauth2.Endpoint{
AuthURL: "https://discord.com/oauth2/authorize",
TokenURL: "https://discord.com/api/oauth2/token",
},
Scopes: []string{"identify", "email"},
}
}
twitterClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitterClientID) twitterClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitterClientID)
if err != nil { if err != nil {
twitterClientID = "" twitterClientID = ""

View File

@ -149,6 +149,12 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
if val, ok := store[constants.EnvKeyAppleClientSecret]; ok { if val, ok := store[constants.EnvKeyAppleClientSecret]; ok {
res.AppleClientSecret = refs.NewStringRef(val.(string)) res.AppleClientSecret = refs.NewStringRef(val.(string))
} }
if val, ok := store[constants.EnvKeyDiscordClientID]; ok {
res.DiscordClientID = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyDiscordClientSecret]; ok {
res.DiscordClientSecret = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyTwitterClientID]; ok { if val, ok := store[constants.EnvKeyTwitterClientID]; ok {
res.TwitterClientID = refs.NewStringRef(val.(string)) res.TwitterClientID = refs.NewStringRef(val.(string))
} }

View File

@ -6,29 +6,29 @@ import (
"strings" "strings"
"time" "time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models" "github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/email" mailService "github.com/authorizerdev/authorizer/server/email"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers" "github.com/authorizerdev/authorizer/server/parsers"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/smsproviders"
"github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators"
) )
// ForgotPasswordResolver is a resolver for forgot password mutation // ForgotPasswordResolver is a resolver for forgot password mutation
func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) { func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error) {
var res *model.Response
gc, err := utils.GinContextFromContext(ctx) gc, err := utils.GinContextFromContext(ctx)
if err != nil { if err != nil {
log.Debug("Failed to get GinContext: ", err) log.Debug("Failed to get GinContext: ", err)
return res, err return nil, err
} }
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
@ -36,74 +36,134 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
log.Debug("Error getting basic auth disabled: ", err) log.Debug("Error getting basic auth disabled: ", err)
isBasicAuthDisabled = true isBasicAuthDisabled = true
} }
if isBasicAuthDisabled { isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
log.Debug("Basic authentication is disabled")
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
params.Email = strings.ToLower(params.Email)
if !validators.IsValidEmail(params.Email) {
log.Debug("Invalid email address: ", params.Email)
return res, fmt.Errorf("invalid email")
}
log := log.WithFields(log.Fields{
"email": params.Email,
})
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
if err != nil { if err != nil {
log.Debug("User not found: ", err) log.Debug("Error getting email verification disabled: ", err)
return res, fmt.Errorf(`user with this email not found`) isEmailVerificationDisabled = true
} }
isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
if err != nil {
log.Debug("Error getting mobile basic auth disabled: ", err)
isMobileBasicAuthDisabled = true
}
isMobileVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
if err != nil {
log.Debug("Error getting mobile verification disabled: ", err)
isMobileVerificationDisabled = true
}
email := refs.StringValue(params.Email)
phoneNumber := refs.StringValue(params.PhoneNumber)
if email == "" && phoneNumber == "" {
log.Debug("Email or phone number is required")
return nil, fmt.Errorf(`email or phone number is required`)
}
log := log.WithFields(log.Fields{
"email": refs.StringValue(params.Email),
"phone_number": refs.StringValue(params.PhoneNumber),
})
isEmailLogin := email != ""
isMobileLogin := phoneNumber != ""
if isBasicAuthDisabled && isEmailLogin && !isEmailVerificationDisabled {
log.Debug("Basic authentication is disabled.")
return nil, fmt.Errorf(`basic authentication is disabled for this instance`)
}
if isMobileBasicAuthDisabled && isMobileLogin && !isMobileVerificationDisabled {
log.Debug("Mobile basic authentication is disabled.")
return nil, fmt.Errorf(`mobile basic authentication is disabled for this instance`)
}
var user *models.User
if isEmailLogin {
user, err = db.Provider.GetUserByEmail(ctx, email)
} else {
user, err = db.Provider.GetUserByPhoneNumber(ctx, phoneNumber)
}
if err != nil {
log.Debug("Failed to get user: ", err)
return nil, fmt.Errorf(`bad user credentials`)
}
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
_, nonceHash, err := utils.GenerateNonce() _, nonceHash, err := utils.GenerateNonce()
if err != nil { if err != nil {
log.Debug("Failed to generate nonce: ", err) log.Debug("Failed to generate nonce: ", err)
return res, err return nil, err
} }
if user.RevokedTimestamp != nil {
redirectURI := "" log.Debug("User access is revoked")
// give higher preference to params redirect uri return nil, fmt.Errorf(`user access has been revoked`)
if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" { }
redirectURI = refs.StringValue(params.RedirectURI) if isEmailLogin {
} else { redirectURI := ""
redirectURI, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL) // give higher preference to params redirect uri
if err != nil { if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" {
log.Debug("ResetPasswordURL not found using default app url: ", err) redirectURI = refs.StringValue(params.RedirectURI)
redirectURI = hostname + "/app/reset-password" } else {
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, redirectURI) redirectURI, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
if err != nil {
log.Debug("ResetPasswordURL not found using default app url: ", err)
redirectURI = hostname + "/app/reset-password"
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, redirectURI)
}
} }
verificationToken, err := token.CreateVerificationToken(email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI)
if err != nil {
log.Debug("Failed to create verification token", err)
return nil, err
}
_, err = db.Provider.AddVerificationRequest(ctx, &models.VerificationRequest{
Token: verificationToken,
Identifier: constants.VerificationTypeForgotPassword,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: email,
Nonce: nonceHash,
RedirectURI: redirectURI,
})
if err != nil {
log.Debug("Failed to add verification request", err)
return nil, err
}
// execute it as go routine so that we can reduce the api latency
go mailService.SendEmail([]string{email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
"user": user.ToMap(),
"organization": utils.GetOrganization(),
"verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI),
})
return &model.ForgotPasswordResponse{
Message: `Please check your inbox! We have sent a password reset link.`,
}, nil
} }
if isMobileLogin {
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI) expiresAt := time.Now().Add(1 * time.Minute).Unix()
if err != nil { otp := utils.GenerateOTP()
log.Debug("Failed to create verification token", err) otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
return res, err Email: refs.StringValue(user.Email),
PhoneNumber: refs.StringValue(user.PhoneNumber),
Otp: otp,
ExpiresAt: expiresAt,
})
if err != nil {
log.Debug("Failed to add otp: ", err)
return nil, err
}
mfaSession := uuid.NewString()
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expiresAt)
if err != nil {
log.Debug("Failed to add mfasession: ", err)
return nil, err
}
cookie.SetMfaSession(gc, mfaSession)
smsBody := strings.Builder{}
smsBody.WriteString("Your verification code is: ")
smsBody.WriteString(otpData.Otp)
if err := smsproviders.SendSMS(phoneNumber, smsBody.String()); err != nil {
log.Debug("Failed to send sms: ", err)
// continue
}
return &model.ForgotPasswordResponse{
Message: "Please enter the OTP sent to your phone number and change your password.",
ShouldShowMobileOtpScreen: refs.NewBoolRef(true),
}, nil
} }
_, err = db.Provider.AddVerificationRequest(ctx, &models.VerificationRequest{ return nil, fmt.Errorf(`email or phone number verification needs to be enabled`)
Token: verificationToken,
Identifier: constants.VerificationTypeForgotPassword,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: params.Email,
Nonce: nonceHash,
RedirectURI: redirectURI,
})
if err != nil {
log.Debug("Failed to add verification request", err)
return res, err
}
// execute it as go routine so that we can reduce the api latency
go email.SendEmail([]string{params.Email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
"user": user.ToMap(),
"organization": utils.GetOrganization(),
"verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI),
})
res = &model.Response{
Message: `Please check your inbox! We have sent a password reset link.`,
}
return res, nil
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/authorizerdev/authorizer/server/cookie" "github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models" "github.com/authorizerdev/authorizer/server/db/models"
mailService "github.com/authorizerdev/authorizer/server/email"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
@ -23,8 +24,6 @@ import (
"github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators" "github.com/authorizerdev/authorizer/server/validators"
mailService "github.com/authorizerdev/authorizer/server/email"
) )
// LoginResolver is a resolver for login mutation // LoginResolver is a resolver for login mutation
@ -172,9 +171,10 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
generateOTP := func(expiresAt int64) (*models.OTP, error) { generateOTP := func(expiresAt int64) (*models.OTP, error) {
otp := utils.GenerateOTP() otp := utils.GenerateOTP()
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{ otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
Email: refs.StringValue(user.Email), Email: refs.StringValue(user.Email),
Otp: otp, PhoneNumber: refs.StringValue(user.PhoneNumber),
ExpiresAt: expiresAt, Otp: otp,
ExpiresAt: expiresAt,
}) })
if err != nil { if err != nil {
log.Debug("Failed to add otp: ", err) log.Debug("Failed to add otp: ", err)

View File

@ -9,8 +9,10 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/crypto" "github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers" "github.com/authorizerdev/authorizer/server/parsers"
@ -29,97 +31,155 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
log.Debug("Failed to get GinContext: ", err) log.Debug("Failed to get GinContext: ", err)
return res, err return res, err
} }
verifyingToken := refs.StringValue(params.Token)
otp := refs.StringValue(params.Otp)
if verifyingToken == "" && otp == "" {
log.Debug("Token or OTP is required")
return res, fmt.Errorf(`token or otp is required`)
}
isTokenVerification := verifyingToken != ""
isOtpVerification := otp != ""
if isOtpVerification && refs.StringValue(params.PhoneNumber) == "" {
log.Debug("Phone number is required")
return res, fmt.Errorf(`phone number is required`)
}
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
if err != nil { if err != nil {
log.Debug("Error getting basic auth disabled: ", err) log.Debug("Error getting basic auth disabled: ", err)
isBasicAuthDisabled = true isBasicAuthDisabled = true
} }
if isBasicAuthDisabled { isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
if err != nil {
log.Debug("Error getting mobile basic auth disabled: ", err)
isBasicAuthDisabled = true
}
if isTokenVerification && isBasicAuthDisabled {
log.Debug("Basic authentication is disabled") log.Debug("Basic authentication is disabled")
return res, fmt.Errorf(`basic authentication is disabled for this instance`) return res, fmt.Errorf(`basic authentication is disabled for this instance`)
} }
if isOtpVerification && isMobileBasicAuthDisabled {
verificationRequest, err := db.Provider.GetVerificationRequestByToken(ctx, params.Token) log.Debug("Mobile basic authentication is disabled")
if err != nil { return res, fmt.Errorf(`mobile basic authentication is disabled for this instance`)
log.Debug("Failed to get verification request: ", err)
return res, fmt.Errorf(`invalid token`)
} }
email := ""
phoneNumber := refs.StringValue(params.PhoneNumber)
var user *models.User
var verificationRequest *models.VerificationRequest
var otpRequest *models.OTP
if isTokenVerification {
verificationRequest, err = db.Provider.GetVerificationRequestByToken(ctx, verifyingToken)
if err != nil {
log.Debug("Failed to get verification request: ", err)
return res, fmt.Errorf(`invalid token`)
}
// verify if token exists in db
hostname := parsers.GetHost(gc)
claim, err := token.ParseJWTToken(verifyingToken)
if err != nil {
log.Debug("Failed to parse token: ", err)
return res, fmt.Errorf(`invalid token`)
}
if ok, err := token.ValidateJWTClaims(claim, hostname, verificationRequest.Nonce, verificationRequest.Email); !ok || err != nil {
log.Debug("Failed to validate jwt claims: ", err)
return res, fmt.Errorf(`invalid token`)
}
email = claim["sub"].(string)
user, err = db.Provider.GetUserByEmail(ctx, email)
if err != nil {
log.Debug("Failed to get user: ", err)
return res, err
}
}
if isOtpVerification {
mfaSession, err := cookie.GetMfaSession(gc)
if err != nil {
log.Debug("Failed to get otp request by email: ", err)
return res, fmt.Errorf(`invalid session: %s`, err.Error())
}
// Get user by phone number
user, err = db.Provider.GetUserByPhoneNumber(ctx, phoneNumber)
if err != nil {
log.Debug("Failed to get user by phone number: ", err)
return res, fmt.Errorf(`user not found`)
}
if _, err := memorystore.Provider.GetMfaSession(user.ID, mfaSession); err != nil {
log.Debug("Failed to get mfa session: ", err)
return res, fmt.Errorf(`invalid session: %s`, err.Error())
}
otpRequest, err = db.Provider.GetOTPByPhoneNumber(ctx, phoneNumber)
if err != nil {
log.Debug("Failed to get otp request by phone number: ", err)
return res, fmt.Errorf(`invalid otp`)
}
if otpRequest.Otp != otp {
log.Debug("Failed to verify otp request: Incorrect value")
return res, fmt.Errorf(`invalid otp`)
}
}
if params.Password != params.ConfirmPassword { if params.Password != params.ConfirmPassword {
log.Debug("Passwords do not match") log.Debug("Passwords do not match")
return res, fmt.Errorf(`passwords don't match`) return res, fmt.Errorf(`passwords don't match`)
} }
if err := validators.IsValidPassword(params.Password); err != nil { if err := validators.IsValidPassword(params.Password); err != nil {
log.Debug("Invalid password") log.Debug("Invalid password")
return res, err return res, err
} }
// verify if token exists in db
hostname := parsers.GetHost(gc)
claim, err := token.ParseJWTToken(params.Token)
if err != nil {
log.Debug("Failed to parse token: ", err)
return res, fmt.Errorf(`invalid token`)
}
if ok, err := token.ValidateJWTClaims(claim, hostname, verificationRequest.Nonce, verificationRequest.Email); !ok || err != nil {
log.Debug("Failed to validate jwt claims: ", err)
return res, fmt.Errorf(`invalid token`)
}
email := claim["sub"].(string)
log := log.WithFields(log.Fields{ log := log.WithFields(log.Fields{
"email": email, "email": email,
"phone": phoneNumber,
}) })
user, err := db.Provider.GetUserByEmail(ctx, email)
if err != nil {
log.Debug("Failed to get user: ", err)
return res, err
}
password, _ := crypto.EncryptPassword(params.Password) password, _ := crypto.EncryptPassword(params.Password)
user.Password = &password user.Password = &password
signupMethod := user.SignupMethods signupMethod := user.SignupMethods
if !strings.Contains(signupMethod, constants.AuthRecipeMethodBasicAuth) { if !strings.Contains(signupMethod, constants.AuthRecipeMethodBasicAuth) && isTokenVerification {
signupMethod = signupMethod + "," + constants.AuthRecipeMethodBasicAuth signupMethod = signupMethod + "," + constants.AuthRecipeMethodBasicAuth
// helpful if user has not signed up with basic auth
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication) if user.EmailVerifiedAt == nil {
if err != nil { now := time.Now().Unix()
log.Debug("MFA service not enabled: ", err) user.EmailVerifiedAt = &now
isMFAEnforced = false
} }
}
if isMFAEnforced { if !strings.Contains(signupMethod, constants.AuthRecipeMethodMobileOTP) && isOtpVerification {
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true) signupMethod = signupMethod + "," + constants.AuthRecipeMethodMobileOTP
// helpful if user has not signed up with basic auth
if user.PhoneNumberVerifiedAt == nil {
now := time.Now().Unix()
user.PhoneNumberVerifiedAt = &now
} }
} }
user.SignupMethods = signupMethod user.SignupMethods = signupMethod
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)
// helpful if user has not signed up with basic auth
if user.EmailVerifiedAt == nil {
now := time.Now().Unix()
user.EmailVerifiedAt = &now
}
// delete from verification table
err = db.Provider.DeleteVerificationRequest(ctx, verificationRequest)
if err != nil { if err != nil {
log.Debug("Failed to delete verification request: ", err) log.Debug("MFA service not enabled: ", err)
return res, err isMFAEnforced = false
}
if isMFAEnforced {
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true)
} }
_, err = db.Provider.UpdateUser(ctx, user) _, err = db.Provider.UpdateUser(ctx, user)
if err != nil { if err != nil {
log.Debug("Failed to update user: ", err) log.Debug("Failed to update user: ", err)
return res, err return res, err
} }
if isTokenVerification {
// delete from verification table
err = db.Provider.DeleteVerificationRequest(ctx, verificationRequest)
if err != nil {
log.Debug("Failed to delete verification request: ", err)
return res, err
}
}
if isOtpVerification {
// delete from otp table
err = db.Provider.DeleteOTP(ctx, otpRequest)
if err != nil {
log.Debug("Failed to delete otp request: ", err)
return res, err
}
}
res = &model.Response{ res = &model.Response{
Message: `Password updated successfully.`, Message: `Password updated successfully.`,
} }
return res, nil return res, nil
} }

View File

@ -38,7 +38,7 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
// get session from cookie // get session from cookie
claims, err := token.ValidateBrowserSession(gc, sessionToken) claims, err := token.ValidateBrowserSession(gc, sessionToken)
if err != nil { if err != nil {
log.Debug("Failed to validate session token", err) log.Debug("Failed to validate session token: ", err)
return res, errors.New("unauthorized") return res, errors.New("unauthorized")
} }
userID := claims.Subject userID := claims.Subject

View File

@ -0,0 +1,61 @@
package test
import (
"fmt"
"strings"
"testing"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func forgotPasswordMobileTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should run forgot password for mobile`, func(t *testing.T) {
req, ctx := createContext(s)
phoneNumber := "6240345678"
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
PhoneNumber: refs.NewStringRef(phoneNumber),
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotNil(t, res)
forgotPasswordRes, err := resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{
PhoneNumber: refs.NewStringRef(phoneNumber),
})
assert.Nil(t, err, "no errors for forgot password")
assert.NotNil(t, forgotPasswordRes)
assert.True(t, *forgotPasswordRes.ShouldShowMobileOtpScreen)
otpReq, err := db.Provider.GetOTPByPhoneNumber(ctx, phoneNumber)
assert.Nil(t, err)
mfaSession := uuid.NewString()
memorystore.Provider.SetMfaSession(res.User.ID, mfaSession, time.Now().Add(1*time.Minute).Unix())
cookie := fmt.Sprintf("%s=%s;", constants.MfaCookieName+"_session", mfaSession)
cookie = strings.TrimSuffix(cookie, ";")
req.Header.Set("Cookie", cookie)
// Reset password
resetPasswordRes, err := resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
PhoneNumber: refs.NewStringRef(phoneNumber),
Otp: refs.NewStringRef(otpReq.Otp),
Password: s.TestInfo.Password + "test",
ConfirmPassword: s.TestInfo.Password + "test",
})
assert.Nil(t, err)
assert.NotNil(t, resetPasswordRes)
// Test login
loginRes, err := resolvers.LoginResolver(ctx, model.LoginInput{
PhoneNumber: refs.NewStringRef(phoneNumber),
Password: s.TestInfo.Password + "test",
})
assert.Nil(t, err)
assert.NotNil(t, loginRes)
})
}

View File

@ -24,7 +24,7 @@ func forgotPasswordTest(t *testing.T, s TestSetup) {
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, res) assert.NotNil(t, res)
forgotPasswordRes, err := resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{ forgotPasswordRes, err := resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{
Email: email, Email: refs.NewStringRef(email),
}) })
assert.Nil(t, err, "no errors for forgot password") assert.Nil(t, err, "no errors for forgot password")
assert.NotNil(t, forgotPasswordRes) assert.NotNil(t, forgotPasswordRes)

View File

@ -130,6 +130,7 @@ func TestResolvers(t *testing.T) {
mobileLoginTests(t, s) mobileLoginTests(t, s)
totpLoginTest(t, s) totpLoginTest(t, s)
forgotPasswordTest(t, s) forgotPasswordTest(t, s)
forgotPasswordMobileTest(t, s)
resendVerifyEmailTests(t, s) resendVerifyEmailTests(t, s)
resetPasswordTest(t, s) resetPasswordTest(t, s)
verifyEmailTest(t, s) verifyEmailTest(t, s)

View File

@ -23,37 +23,30 @@ func resetPasswordTest(t *testing.T, s TestSetup) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
_, err = resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{ _, err = resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{
Email: email, Email: refs.NewStringRef(email),
}) })
assert.Nil(t, err, "no errors for forgot password") assert.Nil(t, err, "no errors for forgot password")
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeForgotPassword) verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeForgotPassword)
assert.Nil(t, err, "should get forgot password request") assert.Nil(t, err, "should get forgot password request")
assert.NotNil(t, verificationRequest) assert.NotNil(t, verificationRequest)
_, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{ _, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token, Token: refs.NewStringRef(verificationRequest.Token),
Password: "test1", Password: "test1",
ConfirmPassword: "test", ConfirmPassword: "test",
}) })
assert.NotNil(t, err, "passwords don't match") assert.NotNil(t, err, "passwords don't match")
_, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{ _, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token, Token: refs.NewStringRef(verificationRequest.Token),
Password: "test1", Password: "test1",
ConfirmPassword: "test1", ConfirmPassword: "test1",
}) })
assert.NotNil(t, err, "invalid password") assert.NotNil(t, err, "invalid password")
_, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{ _, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token, Token: refs.NewStringRef(verificationRequest.Token),
Password: "Test@1234", Password: "Test@1234",
ConfirmPassword: "Test@1234", ConfirmPassword: "Test@1234",
}) })
assert.Nil(t, err, "password changed successfully") assert.Nil(t, err, "password changed successfully")
cleanData(email) cleanData(email)
}) })
} }

View File

@ -91,7 +91,6 @@ func CreateAuthToken(gc *gin.Context, user *models.User, roles, scope []string,
AccessToken: &JWTToken{Token: accessToken, ExpiresAt: accessTokenExpiresAt}, AccessToken: &JWTToken{Token: accessToken, ExpiresAt: accessTokenExpiresAt},
IDToken: &JWTToken{Token: idToken, ExpiresAt: idTokenExpiresAt}, IDToken: &JWTToken{Token: idToken, ExpiresAt: idTokenExpiresAt},
} }
if utils.StringSliceContains(scope, "offline_access") { if utils.StringSliceContains(scope, "offline_access") {
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, scope, hostname, nonce, loginMethod) refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, scope, hostname, nonce, loginMethod)
if err != nil { if err != nil {
@ -354,7 +353,7 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
} }
token, err := memorystore.Provider.GetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+res.Nonce) token, err := memorystore.Provider.GetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+res.Nonce)
if token == "" || err != nil { if token == "" || err != nil {
log.Debug("invalid browser session:", err) log.Debugf("invalid browser session: %v, key: %s", err, sessionStoreKey+":"+constants.TokenTypeSessionToken+"_"+res.Nonce)
return nil, fmt.Errorf(`unauthorized`) return nil, fmt.Errorf(`unauthorized`)
} }