feat: Add Discord as Identity Provider
This commit is contained in:
@@ -19,6 +19,8 @@ const (
|
||||
AuthRecipeMethodLinkedIn = "linkedin"
|
||||
// AuthRecipeMethodApple is the apple auth method
|
||||
AuthRecipeMethodApple = "apple"
|
||||
// AuthRecipeMethodDiscord is the discord auth method
|
||||
AuthRecipeMethodDiscord = "discord"
|
||||
// AuthRecipeMethodTwitter is the twitter auth method
|
||||
AuthRecipeMethodTwitter = "twitter"
|
||||
// AuthRecipeMethodMicrosoft is the microsoft auth method
|
||||
|
@@ -108,6 +108,10 @@ const (
|
||||
EnvKeyAppleClientID = "APPLE_CLIENT_ID"
|
||||
// EnvKeyAppleClientSecret key for env variable 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 = "TWITTER_CLIENT_ID"
|
||||
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET
|
||||
|
@@ -17,6 +17,7 @@ const (
|
||||
|
||||
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.
|
||||
// Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo
|
||||
MicrosoftUserInfoURL = "https://graph.microsoft.com/oidc/userinfo"
|
||||
|
@@ -109,6 +109,8 @@ type ComplexityRoot struct {
|
||||
DisableSignUp func(childComplexity int) int
|
||||
DisableStrongPassword func(childComplexity int) int
|
||||
DisableTotpLogin func(childComplexity int) int
|
||||
DiscordClientID func(childComplexity int) int
|
||||
DiscordClientSecret func(childComplexity int) int
|
||||
EnforceMultiFactorAuthentication func(childComplexity int) int
|
||||
FacebookClientID func(childComplexity int) int
|
||||
FacebookClientSecret func(childComplexity int) int
|
||||
@@ -170,6 +172,7 @@ type ComplexityRoot struct {
|
||||
ClientID func(childComplexity int) int
|
||||
IsAppleLoginEnabled func(childComplexity int) int
|
||||
IsBasicAuthenticationEnabled func(childComplexity int) int
|
||||
IsDiscordLoginEnabled func(childComplexity int) int
|
||||
IsEmailVerificationEnabled func(childComplexity int) int
|
||||
IsFacebookLoginEnabled func(childComplexity int) int
|
||||
IsGithubLoginEnabled func(childComplexity int) int
|
||||
@@ -792,6 +795,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.Env.EnforceMultiFactorAuthentication == nil {
|
||||
break
|
||||
@@ -1114,6 +1131,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.Meta.IsEmailVerificationEnabled == nil {
|
||||
break
|
||||
@@ -2381,6 +2405,7 @@ type Meta {
|
||||
is_github_login_enabled: Boolean!
|
||||
is_linkedin_login_enabled: Boolean!
|
||||
is_apple_login_enabled: Boolean!
|
||||
is_discord_login_enabled: Boolean!
|
||||
is_twitter_login_enabled: Boolean!
|
||||
is_microsoft_login_enabled: Boolean!
|
||||
is_twitch_login_enabled: Boolean!
|
||||
@@ -2539,6 +2564,8 @@ type Env {
|
||||
LINKEDIN_CLIENT_SECRET: String
|
||||
APPLE_CLIENT_ID: String
|
||||
APPLE_CLIENT_SECRET: String
|
||||
DISCORD_CLIENT_ID: String
|
||||
DISCORD_CLIENT_SECRET: String
|
||||
TWITTER_CLIENT_ID: String
|
||||
TWITTER_CLIENT_SECRET: String
|
||||
MICROSOFT_CLIENT_ID: String
|
||||
@@ -2668,6 +2695,8 @@ input UpdateEnvInput {
|
||||
LINKEDIN_CLIENT_SECRET: String
|
||||
APPLE_CLIENT_ID: String
|
||||
APPLE_CLIENT_SECRET: String
|
||||
DISCORD_CLIENT_ID: String
|
||||
DISCORD_CLIENT_SECRET: String
|
||||
TWITTER_CLIENT_ID: String
|
||||
TWITTER_CLIENT_SECRET: String
|
||||
MICROSOFT_CLIENT_ID: String
|
||||
@@ -6702,6 +6731,88 @@ func (ec *executionContext) fieldContext_Env_APPLE_CLIENT_SECRET(ctx context.Con
|
||||
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) {
|
||||
fc, err := ec.fieldContext_Env_TWITTER_CLIENT_ID(ctx, field)
|
||||
if err != nil {
|
||||
@@ -8107,6 +8218,50 @@ func (ec *executionContext) fieldContext_Meta_is_apple_login_enabled(ctx context
|
||||
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) {
|
||||
fc, err := ec.fieldContext_Meta_is_twitter_login_enabled(ctx, field)
|
||||
if err != nil {
|
||||
@@ -10855,6 +11010,8 @@ func (ec *executionContext) fieldContext_Query_meta(ctx context.Context, field g
|
||||
return ec.fieldContext_Meta_is_linkedin_login_enabled(ctx, field)
|
||||
case "is_apple_login_enabled":
|
||||
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":
|
||||
return ec.fieldContext_Meta_is_twitter_login_enabled(ctx, field)
|
||||
case "is_microsoft_login_enabled":
|
||||
@@ -11579,6 +11736,10 @@ func (ec *executionContext) fieldContext_Query__env(ctx context.Context, field g
|
||||
return ec.fieldContext_Env_APPLE_CLIENT_ID(ctx, field)
|
||||
case "APPLE_CLIENT_SECRET":
|
||||
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":
|
||||
return ec.fieldContext_Env_TWITTER_CLIENT_ID(ctx, field)
|
||||
case "TWITTER_CLIENT_SECRET":
|
||||
@@ -18127,7 +18288,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", "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 {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -18521,6 +18682,24 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
|
||||
return it, err
|
||||
}
|
||||
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":
|
||||
var err error
|
||||
|
||||
@@ -19556,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)
|
||||
case "APPLE_CLIENT_SECRET":
|
||||
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":
|
||||
out.Values[i] = ec._Env_TWITTER_CLIENT_ID(ctx, field, obj)
|
||||
case "TWITTER_CLIENT_SECRET":
|
||||
@@ -19841,6 +20024,11 @@ func (ec *executionContext) _Meta(ctx context.Context, sel ast.SelectionSet, obj
|
||||
if out.Values[i] == graphql.Null {
|
||||
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":
|
||||
out.Values[i] = ec._Meta_is_twitter_login_enabled(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
|
@@ -114,6 +114,8 @@ type Env struct {
|
||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET,omitempty"`
|
||||
AppleClientID *string `json:"APPLE_CLIENT_ID,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"`
|
||||
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET,omitempty"`
|
||||
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID,omitempty"`
|
||||
@@ -204,6 +206,7 @@ type Meta struct {
|
||||
IsGithubLoginEnabled bool `json:"is_github_login_enabled"`
|
||||
IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"`
|
||||
IsAppleLoginEnabled bool `json:"is_apple_login_enabled"`
|
||||
IsDiscordLoginEnabled bool `json:"is_discord_login_enabled"`
|
||||
IsTwitterLoginEnabled bool `json:"is_twitter_login_enabled"`
|
||||
IsMicrosoftLoginEnabled bool `json:"is_microsoft_login_enabled"`
|
||||
IsTwitchLoginEnabled bool `json:"is_twitch_login_enabled"`
|
||||
@@ -391,6 +394,8 @@ type UpdateEnvInput struct {
|
||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET,omitempty"`
|
||||
AppleClientID *string `json:"APPLE_CLIENT_ID,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"`
|
||||
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET,omitempty"`
|
||||
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID,omitempty"`
|
||||
|
@@ -20,6 +20,7 @@ type Meta {
|
||||
is_github_login_enabled: Boolean!
|
||||
is_linkedin_login_enabled: Boolean!
|
||||
is_apple_login_enabled: Boolean!
|
||||
is_discord_login_enabled: Boolean!
|
||||
is_twitter_login_enabled: Boolean!
|
||||
is_microsoft_login_enabled: Boolean!
|
||||
is_twitch_login_enabled: Boolean!
|
||||
@@ -178,6 +179,8 @@ type Env {
|
||||
LINKEDIN_CLIENT_SECRET: String
|
||||
APPLE_CLIENT_ID: String
|
||||
APPLE_CLIENT_SECRET: String
|
||||
DISCORD_CLIENT_ID: String
|
||||
DISCORD_CLIENT_SECRET: String
|
||||
TWITTER_CLIENT_ID: String
|
||||
TWITTER_CLIENT_SECRET: String
|
||||
MICROSOFT_CLIENT_ID: String
|
||||
@@ -307,6 +310,8 @@ input UpdateEnvInput {
|
||||
LINKEDIN_CLIENT_SECRET: String
|
||||
APPLE_CLIENT_ID: String
|
||||
APPLE_CLIENT_SECRET: String
|
||||
DISCORD_CLIENT_ID: String
|
||||
DISCORD_CLIENT_SECRET: String
|
||||
TWITTER_CLIENT_ID: String
|
||||
TWITTER_CLIENT_SECRET: String
|
||||
MICROSOFT_CLIENT_ID: String
|
||||
|
@@ -73,6 +73,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
user, err = processLinkedInUserInfo(ctx, oauthCode)
|
||||
case constants.AuthRecipeMethodApple:
|
||||
user, err = processAppleUserInfo(ctx, oauthCode)
|
||||
case constants.AuthRecipeMethodDiscord:
|
||||
user, err = processDiscordUserInfo(ctx, oauthCode)
|
||||
case constants.AuthRecipeMethodTwitter:
|
||||
user, err = processTwitterUserInfo(ctx, oauthCode, sessionState)
|
||||
case constants.AuthRecipeMethodMicrosoft:
|
||||
@@ -609,6 +611,71 @@ func processAppleUserInfo(ctx context.Context, code string) (*models.User, error
|
||||
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) {
|
||||
oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(ctx, code, oauth2.SetAuthURLParam("code_verifier", verifier))
|
||||
if err != nil {
|
||||
|
@@ -192,6 +192,24 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||
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"))
|
||||
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:
|
||||
if oauth.OAuthProviders.AppleConfig == nil {
|
||||
log.Debug("Apple OAuth provider is not configured")
|
||||
|
@@ -30,6 +30,7 @@ type OAuthProvider struct {
|
||||
FacebookConfig *oauth2.Config
|
||||
LinkedInConfig *oauth2.Config
|
||||
AppleConfig *oauth2.Config
|
||||
DiscordConfig *oauth2.Config
|
||||
TwitterConfig *oauth2.Config
|
||||
MicrosoftConfig *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)
|
||||
if err != nil {
|
||||
twitterClientID = ""
|
||||
|
@@ -149,6 +149,12 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
|
||||
if val, ok := store[constants.EnvKeyAppleClientSecret]; ok {
|
||||
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 {
|
||||
res.TwitterClientID = refs.NewStringRef(val.(string))
|
||||
}
|
||||
|
Reference in New Issue
Block a user