From b2cfad53b4dd00cd7f82a29bd16b4fb552441088 Mon Sep 17 00:00:00 2001 From: badolamgk Date: Wed, 2 Oct 2024 23:34:59 +0530 Subject: [PATCH 1/6] feature: add discord integration --- README.md | 9 ++++++++- _example/frontend/index.html | 4 ++-- _example/main.go | 4 ++++ auth.go | 2 ++ auth_test.go | 3 ++- provider/providers.go | 28 ++++++++++++++++++++++++++++ provider/providers_test.go | 13 +++++++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d273ddb1..83ed57f7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # auth - authentication via oauth2, direct and email [![Build Status](https://github.com/go-pkgz/auth/workflows/build/badge.svg)](https://github.com/go-pkgz/auth/actions) [![Coverage Status](https://coveralls.io/repos/github/go-pkgz/auth/badge.svg?branch=master)](https://coveralls.io/github/go-pkgz/auth?branch=master) [![godoc](https://godoc.org/github.com/go-pkgz/auth?status.svg)](https://pkg.go.dev/github.com/go-pkgz/auth?tab=doc) -This library provides "social login" with Github, Google, Facebook, Microsoft, Twitter, Yandex, Battle.net, Apple, Patreon and Telegram as well as custom auth providers and email verification. +This library provides "social login" with Github, Google, Facebook, Microsoft, Twitter, Yandex, Battle.net, Apple, Patreon, Discord and Telegram as well as custom auth providers and email verification. - Multiple oauth2 providers can be used at the same time - Special `dev` provider allows local testing and development @@ -604,6 +604,13 @@ For more details refer to [Complete Guide of Battle.net OAuth API and Login Butt 1. Under **"Redirect URIs"** enter the correct url constructed as domain + `/auth/patreon/callback`. ie `https://example.mysite.com/auth/patreon/callback` 1. Take note of the **Client ID** and **Client Secret** +#### Discord Auth Provider #### +1. Log into Discord Developer Portal https://discord.com/developers/applications +2. Click on **New Application** to create the application required for Oauth +3. After filling **"NAME"**, navigate to **"OAuth2"** option on the left sidebar +4. Under **"Redirects"** enter the correct url constructed as domain + `/auth/discord/callback`. ie `https://remark42.mysite.com/auth/discord/callback` +5. Take note of the **CLIENT ID** and **CLIENT SECRET** + #### Twitter Auth Provider 1. Create a new twitter application https://developer.twitter.com/en/apps 1. Fill **App name** and **Description** and **URL** of your site diff --git a/_example/frontend/index.html b/_example/frontend/index.html index e51fc961..128ecb35 100644 --- a/_example/frontend/index.html +++ b/_example/frontend/index.html @@ -10,7 +10,7 @@

GO-PKGZ/AUTHExample page

-

This library provides “social login” with Github, Google, Microsoft, Facebook, Yandex, Battle.net, Patreon and Telegram.

+

This library provides “social login” with Github, Google, Microsoft, Facebook, Yandex, Battle.net, Patreon, Discord and Telegram.

Status: unauthorized
@@ -25,4 +25,4 @@

GO-PKGZ/AUTHgo-pkgz/auth - \ No newline at end of file + diff --git a/_example/main.go b/_example/main.go index 3dd315c8..08034958 100644 --- a/_example/main.go +++ b/_example/main.go @@ -66,6 +66,9 @@ func main() { if strings.HasPrefix(claims.User.ID, "patreon_") { // allow all users with ms auth return true } + if strings.HasPrefix(claims.User.ID, "discord_") { // allow all users with ms auth + return true + } if strings.HasPrefix(claims.User.Name, "dev_") { // non-guthub allow only dev_* names return true } @@ -84,6 +87,7 @@ func main() { service.AddProvider("twitter", os.Getenv("AEXMPL_TWITTER_APIKEY"), os.Getenv("AEXMPL_TWITTER_APISEC")) service.AddProvider("microsoft", os.Getenv("AEXMPL_MS_APIKEY"), os.Getenv("AEXMPL_MS_APISEC")) service.AddProvider("patreon", os.Getenv("AEXMPL_PATREON_CID"), os.Getenv("AEXMPL_PATREON_CSEC")) + service.AddProvider("discord", os.Getenv("AEXMPL_DISCORD_CID"), os.Getenv("AEXMPL_DISCORD_CSEC")) // allow sign with apple id appleCfg := provider.AppleConfig{ diff --git a/auth.go b/auth.go index 5bd9d879..33366b20 100644 --- a/auth.go +++ b/auth.go @@ -257,6 +257,8 @@ func (s *Service) addProviderByName(name string, p provider.Params) { prov = provider.NewTwitter(p) case "patreon": prov = provider.NewPatreon(p) + case "discord": + prov = provider.NewDiscord(p) case "dev": prov = provider.NewDev(p) default: diff --git a/auth_test.go b/auth_test.go index 9f9fbece..3be93721 100644 --- a/auth_test.go +++ b/auth_test.go @@ -61,6 +61,7 @@ func TestProvider(t *testing.T) { svc.AddProvider("twitter", "cid", "csecret") svc.AddProvider("battlenet", "cid", "csecret") svc.AddProvider("patreon", "cid", "csecret") + svc.AddProvider("discord", "cid", "csecret") svc.AddProvider("bad", "cid", "csecret") c := customHandler{} @@ -81,7 +82,7 @@ func TestProvider(t *testing.T) { assert.Equal(t, "github", op.Name()) pp := svc.Providers() - assert.Equal(t, 10, len(pp)) + assert.Equal(t, 11, len(pp)) ch, err := svc.Provider("telegramBotMySiteCom") assert.NoError(t, err) diff --git a/provider/providers.go b/provider/providers.go index 4487b4cb..ec49466f 100644 --- a/provider/providers.go +++ b/provider/providers.go @@ -265,3 +265,31 @@ func NewPatreon(p Params) Oauth2Handler { }, }) } + +func NewDiscord(p Params) Oauth2Handler { + return initOauth2Handler(p, Oauth2Handler{ + name: "discord", + // see https://discord.com/developers/docs/topics/oauth2 + endpoint: oauth2.Endpoint{ + AuthURL: "https://discord.com/oauth2/authorize", + TokenURL: "https://discord.com/api/oauth2/token", + }, + infoURL: "https://discord.com/api/v10/users/@me", + scopes: []string{"email", "identify"}, + mapUser: func(data UserData, _ []byte) token.User { + userInfo := token.User{ + ID: "discord_" + token.HashID(sha1.New(), data.Value("id")), + Name: data.Value("username"), + Picture: fmt.Sprintf("https://cdn.discordapp.com/avatars/%s/%s.webp", data.Value("id"), data.Value("avatar")), + } + if data.Value("email") != "" { + userInfo.Email = data.Value("email") + } + + for k, v := range p.UserAttributes { + userInfo.SetStrAttr(v, data.Value(k)) + } + return userInfo + }, + }) +} diff --git a/provider/providers_test.go b/provider/providers_test.go index 88ea28ce..5923cf01 100644 --- a/provider/providers_test.go +++ b/provider/providers_test.go @@ -208,3 +208,16 @@ func TestProviders_NewPatreon(t *testing.T) { user, ) } + +func TestProviders_NewDiscord(t *testing.T) { + r := NewDiscord(Params{URL: "http://demo.remark42.com", Cid: "cid", Csecret: "cs"}) + assert.Equal(t, "discord", r.Name()) + + t.Run("With all data", func(t *testing.T) { + udata := UserData{"id": "248533295981532", "username": "test_user", "avatar": "374384984773", "email": "test@email.com"} + user := r.mapUser(udata, nil) + assert.Equal(t, token.User{Name: "test_user", ID: "discord_9b472605c1318483fb4b88f9acf22cdd4219f9a0", Email: "test@email.com", + Picture: "https://cdn.discordapp.com/avatars/248533295981532/374384984773.webp"}, user, "got %+v", user) + }) + +} From a4b73f98dafebcecf8713f532dd575e4ce0fe5fe Mon Sep 17 00:00:00 2001 From: badolamgk Date: Thu, 3 Oct 2024 00:18:15 +0530 Subject: [PATCH 2/6] fix: linting issue on NewDiscord --- provider/providers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/provider/providers.go b/provider/providers.go index ec49466f..32b53aa2 100644 --- a/provider/providers.go +++ b/provider/providers.go @@ -266,6 +266,7 @@ func NewPatreon(p Params) Oauth2Handler { }) } +// NewDiscord makes discord oauth2 provider func NewDiscord(p Params) Oauth2Handler { return initOauth2Handler(p, Oauth2Handler{ name: "discord", From 1a2c1450aacd038a41bbed86379b2cf6b114690f Mon Sep 17 00:00:00 2001 From: badolamgk Date: Thu, 3 Oct 2024 08:41:00 +0530 Subject: [PATCH 3/6] fixes: add provider to v2, remove email scope and cleanup indent in README --- README.md | 2 +- provider/providers.go | 5 +---- v2/auth.go | 2 ++ v2/auth_test.go | 3 ++- v2/provider/providers.go | 26 ++++++++++++++++++++++++++ v2/provider/providers_test.go | 13 +++++++++++++ 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 83ed57f7..9e48ae4c 100644 --- a/README.md +++ b/README.md @@ -605,7 +605,7 @@ For more details refer to [Complete Guide of Battle.net OAuth API and Login Butt 1. Take note of the **Client ID** and **Client Secret** #### Discord Auth Provider #### -1. Log into Discord Developer Portal https://discord.com/developers/applications +1. Log into Discord Developer Portal https://discord.com/developers/applications 2. Click on **New Application** to create the application required for Oauth 3. After filling **"NAME"**, navigate to **"OAuth2"** option on the left sidebar 4. Under **"Redirects"** enter the correct url constructed as domain + `/auth/discord/callback`. ie `https://remark42.mysite.com/auth/discord/callback` diff --git a/provider/providers.go b/provider/providers.go index 32b53aa2..9711781f 100644 --- a/provider/providers.go +++ b/provider/providers.go @@ -276,16 +276,13 @@ func NewDiscord(p Params) Oauth2Handler { TokenURL: "https://discord.com/api/oauth2/token", }, infoURL: "https://discord.com/api/v10/users/@me", - scopes: []string{"email", "identify"}, + scopes: []string{"identify"}, mapUser: func(data UserData, _ []byte) token.User { userInfo := token.User{ ID: "discord_" + token.HashID(sha1.New(), data.Value("id")), Name: data.Value("username"), Picture: fmt.Sprintf("https://cdn.discordapp.com/avatars/%s/%s.webp", data.Value("id"), data.Value("avatar")), } - if data.Value("email") != "" { - userInfo.Email = data.Value("email") - } for k, v := range p.UserAttributes { userInfo.SetStrAttr(v, data.Value(k)) diff --git a/v2/auth.go b/v2/auth.go index 30d4a613..9077b2d5 100644 --- a/v2/auth.go +++ b/v2/auth.go @@ -257,6 +257,8 @@ func (s *Service) addProviderByName(name string, p provider.Params) { prov = provider.NewTwitter(p) case "patreon": prov = provider.NewPatreon(p) + case "discord": + prov = provider.NewDiscord(p) case "dev": prov = provider.NewDev(p) default: diff --git a/v2/auth_test.go b/v2/auth_test.go index 0031d55e..3e5ddbbe 100644 --- a/v2/auth_test.go +++ b/v2/auth_test.go @@ -61,6 +61,7 @@ func TestProvider(t *testing.T) { svc.AddProvider("twitter", "cid", "csecret") svc.AddProvider("battlenet", "cid", "csecret") svc.AddProvider("patreon", "cid", "csecret") + svc.AddProvider("discord", "cid", "csecret") svc.AddProvider("bad", "cid", "csecret") c := customHandler{} @@ -81,7 +82,7 @@ func TestProvider(t *testing.T) { assert.Equal(t, "github", op.Name()) pp := svc.Providers() - assert.Equal(t, 10, len(pp)) + assert.Equal(t, 11, len(pp)) ch, err := svc.Provider("telegramBotMySiteCom") assert.NoError(t, err) diff --git a/v2/provider/providers.go b/v2/provider/providers.go index 5f8758e0..471fa312 100644 --- a/v2/provider/providers.go +++ b/v2/provider/providers.go @@ -265,3 +265,29 @@ func NewPatreon(p Params) Oauth2Handler { }, }) } + +// NewDiscord makes discord oauth2 provider +func NewDiscord(p Params) Oauth2Handler { + return initOauth2Handler(p, Oauth2Handler{ + name: "discord", + // see https://discord.com/developers/docs/topics/oauth2 + endpoint: oauth2.Endpoint{ + AuthURL: "https://discord.com/oauth2/authorize", + TokenURL: "https://discord.com/api/oauth2/token", + }, + infoURL: "https://discord.com/api/v10/users/@me", + scopes: []string{"identify"}, + mapUser: func(data UserData, _ []byte) token.User { + userInfo := token.User{ + ID: "discord_" + token.HashID(sha1.New(), data.Value("id")), + Name: data.Value("username"), + Picture: fmt.Sprintf("https://cdn.discordapp.com/avatars/%s/%s.webp", data.Value("id"), data.Value("avatar")), + } + + for k, v := range p.UserAttributes { + userInfo.SetStrAttr(v, data.Value(k)) + } + return userInfo + }, + }) +} diff --git a/v2/provider/providers_test.go b/v2/provider/providers_test.go index 7d3b8fed..cfdc0b6d 100644 --- a/v2/provider/providers_test.go +++ b/v2/provider/providers_test.go @@ -208,3 +208,16 @@ func TestProviders_NewPatreon(t *testing.T) { user, ) } + +func TestProviders_NewDiscord(t *testing.T) { + r := NewDiscord(Params{URL: "http://demo.remark42.com", Cid: "cid", Csecret: "cs"}) + assert.Equal(t, "discord", r.Name()) + + t.Run("With all data", func(t *testing.T) { + udata := UserData{"id": "248533295981532", "username": "test_user", "avatar": "374384984773", "email": "test@email.com"} + user := r.mapUser(udata, nil) + assert.Equal(t, token.User{Name: "test_user", ID: "discord_9b472605c1318483fb4b88f9acf22cdd4219f9a0", Email: "test@email.com", + Picture: "https://cdn.discordapp.com/avatars/248533295981532/374384984773.webp"}, user, "got %+v", user) + }) + +} From 53f14a093e1f9ec403344cc1348873cee0df07a7 Mon Sep 17 00:00:00 2001 From: badolamgk Date: Thu, 3 Oct 2024 10:04:04 +0530 Subject: [PATCH 4/6] fix: comment on token validator --- _example/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_example/main.go b/_example/main.go index 08034958..dd9c5857 100644 --- a/_example/main.go +++ b/_example/main.go @@ -63,10 +63,10 @@ func main() { if strings.HasPrefix(claims.User.ID, "microsoft_") { // allow all users with ms auth return true } - if strings.HasPrefix(claims.User.ID, "patreon_") { // allow all users with ms auth + if strings.HasPrefix(claims.User.ID, "patreon_") { // allow all users with patreon auth return true } - if strings.HasPrefix(claims.User.ID, "discord_") { // allow all users with ms auth + if strings.HasPrefix(claims.User.ID, "discord_") { // allow all users with discord auth return true } if strings.HasPrefix(claims.User.Name, "dev_") { // non-guthub allow only dev_* names From a6ae640e2ac88ed100b1b5736efd07ab2b184a4a Mon Sep 17 00:00:00 2001 From: badolamgk Date: Sun, 6 Oct 2024 00:41:27 +0530 Subject: [PATCH 5/6] fix: changes involving email removal in test script --- v2/provider/providers_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/provider/providers_test.go b/v2/provider/providers_test.go index cfdc0b6d..f3b91a8a 100644 --- a/v2/provider/providers_test.go +++ b/v2/provider/providers_test.go @@ -214,9 +214,9 @@ func TestProviders_NewDiscord(t *testing.T) { assert.Equal(t, "discord", r.Name()) t.Run("With all data", func(t *testing.T) { - udata := UserData{"id": "248533295981532", "username": "test_user", "avatar": "374384984773", "email": "test@email.com"} + udata := UserData{"id": "248533295981532", "username": "test_user", "avatar": "374384984773"} user := r.mapUser(udata, nil) - assert.Equal(t, token.User{Name: "test_user", ID: "discord_9b472605c1318483fb4b88f9acf22cdd4219f9a0", Email: "test@email.com", + assert.Equal(t, token.User{Name: "test_user", ID: "discord_9b472605c1318483fb4b88f9acf22cdd4219f9a0", Picture: "https://cdn.discordapp.com/avatars/248533295981532/374384984773.webp"}, user, "got %+v", user) }) From e91cd25b80c42d5489d20e550c7365204c95c589 Mon Sep 17 00:00:00 2001 From: badolamgk Date: Sun, 6 Oct 2024 00:43:37 +0530 Subject: [PATCH 6/6] fix: changes involving email removal in test script (v1) --- provider/providers_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/providers_test.go b/provider/providers_test.go index 5923cf01..8c2a5a48 100644 --- a/provider/providers_test.go +++ b/provider/providers_test.go @@ -214,9 +214,9 @@ func TestProviders_NewDiscord(t *testing.T) { assert.Equal(t, "discord", r.Name()) t.Run("With all data", func(t *testing.T) { - udata := UserData{"id": "248533295981532", "username": "test_user", "avatar": "374384984773", "email": "test@email.com"} + udata := UserData{"id": "248533295981532", "username": "test_user", "avatar": "374384984773"} user := r.mapUser(udata, nil) - assert.Equal(t, token.User{Name: "test_user", ID: "discord_9b472605c1318483fb4b88f9acf22cdd4219f9a0", Email: "test@email.com", + assert.Equal(t, token.User{Name: "test_user", ID: "discord_9b472605c1318483fb4b88f9acf22cdd4219f9a0", Picture: "https://cdn.discordapp.com/avatars/248533295981532/374384984773.webp"}, user, "got %+v", user) })