Skip to content

Commit

Permalink
feature: Discord OAuth2 Provider (#223)
Browse files Browse the repository at this point in the history
* feature: add discord integration

* fix: linting issue on NewDiscord

* fixes: add provider to v2, remove email scope and cleanup indent in README

* fix: comment on token validator

* fix: changes involving email removal in test script

* fix: changes involving email removal in test script (v1)

---------

Co-authored-by: badolamgk <[email protected]>
  • Loading branch information
mgkbadola and badolamgk authored Oct 7, 2024
1 parent 08bfdf2 commit 78537e6
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 6 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions _example/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<body>
<header class="main-header">
<h1 class="title"><a href="https://github.com/go-pkgz/auth">GO-PKGZ/AUTH</a><span class="pretitle">Example page</span></h1>
<p class="description">This library provides “social login” with Github, Google, Microsoft, Facebook, Yandex, Battle.net, Patreon and Telegram.</p>
<p class="description">This library provides “social login” with Github, Google, Microsoft, Facebook, Yandex, Battle.net, Patreon, Discord and Telegram.</p>
<div class="login"></div>
</header>
<div class="status"><span class="minititle">Status:</span> <span class="status__label">unauthorized</span></div>
Expand All @@ -25,4 +25,4 @@ <h1 class="title"><a href="https://github.com/go-pkgz/auth">GO-PKGZ/AUTH</a><spa
<a href="https://github.com/go-pkgz/auth">go-pkgz/auth</a></div>
<script src="./main.js?v=2"></script>
</body>
</html>
</html>
6 changes: 5 additions & 1 deletion _example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +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 discord auth
return true
}
if strings.HasPrefix(claims.User.Name, "dev_") { // non-guthub allow only dev_* names
Expand All @@ -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{
Expand Down
2 changes: 2 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -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)
Expand Down
26 changes: 26 additions & 0 deletions provider/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
})
}
13 changes: 13 additions & 0 deletions provider/providers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
user := r.mapUser(udata, nil)
assert.Equal(t, token.User{Name: "test_user", ID: "discord_9b472605c1318483fb4b88f9acf22cdd4219f9a0",
Picture: "https://cdn.discordapp.com/avatars/248533295981532/374384984773.webp"}, user, "got %+v", user)
})

}
2 changes: 2 additions & 0 deletions v2/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion v2/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -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)
Expand Down
26 changes: 26 additions & 0 deletions v2/provider/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
})
}
13 changes: 13 additions & 0 deletions v2/provider/providers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
user := r.mapUser(udata, nil)
assert.Equal(t, token.User{Name: "test_user", ID: "discord_9b472605c1318483fb4b88f9acf22cdd4219f9a0",
Picture: "https://cdn.discordapp.com/avatars/248533295981532/374384984773.webp"}, user, "got %+v", user)
})

}

0 comments on commit 78537e6

Please sign in to comment.