Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Twitch OAuth2 Provider #230

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
*.out
.vscode
*.cov
Dockerfile
Dockerfile
.idea
8 changes: 7 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, Discord 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, Telegram and Twitch 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 @@ -617,6 +617,12 @@ For more details refer to [Complete Guide of Battle.net OAuth API and Login Butt
1. In the field **Callback URLs** enter the correct url of your callback handler e.g. https://example.mysite.com/{route}/twitter/callback
1. Under **Key and tokens** take note of the **Consumer API Key** and **Consumer API Secret key**. Those will be used as `cid` and `csecret`

#### Twitch Auth Provider ####
1. Create a new Twitch application https://dev.twitch.tv/console/apps/create
2. Fill **"Name"**, **"OAuth Redirect URL"** and choose category
3. Take note of the **Client ID** and **Client Secret**
4. Detailed instructions, step by step https://dev.twitch.tv/docs/authentication/register-app/

## XSRF Protections
By default, the XSRF protections will apply to all requests which reach the `middlewares.Auth`,
`middlewares.Admin` or `middlewares.RBAC` middlewares. This will require setting a request header
Expand Down
2 changes: 2 additions & 0 deletions v2/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ func (s *Service) addProviderByName(name string, p provider.Params) {
prov = provider.NewPatreon(p)
case "discord":
prov = provider.NewDiscord(p)
case "twitch":
prov = provider.NewTwitch(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 @@ -62,6 +62,7 @@ func TestProvider(t *testing.T) {
svc.AddProvider("battlenet", "cid", "csecret")
svc.AddProvider("patreon", "cid", "csecret")
svc.AddProvider("discord", "cid", "csecret")
svc.AddProvider("twitch", "cid", "csecret")
svc.AddProvider("bad", "cid", "csecret")

c := customHandler{}
Expand All @@ -82,7 +83,7 @@ func TestProvider(t *testing.T) {
assert.Equal(t, "github", op.Name())

pp := svc.Providers()
assert.Equal(t, 11, len(pp))
assert.Equal(t, 12, len(pp))

ch, err := svc.Provider("telegramBotMySiteCom")
assert.NoError(t, err)
Expand Down
23 changes: 3 additions & 20 deletions v2/provider/apple.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,7 @@ const (

// appleVerificationResponse is based on https://developer.apple.com/documentation/signinwithapplerestapi/tokenresponse
type appleVerificationResponse struct {
// A token used to access allowed user data, but now not implemented public interface for it.
AccessToken string `json:"access_token"`

// Access token type, always equal the "bearer".
TokenType string `json:"token_type"`

// Access token expires time in seconds. Always equal 3600 seconds (1 hour)
ExpiresIn int `json:"expires_in"`

// The refresh token used to regenerate new access tokens.
RefreshToken string `json:"refresh_token"`
AccessTokenResponse

// Main JSON Web Token that contains the user’s identity information.
IDToken string `json:"id_token"`
Expand Down Expand Up @@ -323,7 +313,7 @@ func (ah AppleHandler) AuthHandler(w http.ResponseWriter, r *http.Request) {
}

var resp appleVerificationResponse
err = ah.exchange(context.Background(), code, ah.makeRedirURL(r.URL.Path), &resp)
err = ah.exchange(context.Background(), code, makeRedirectURL(ah.URL, r.URL.Path), &resp)
if err != nil {
rest.SendErrorJSON(w, r, ah.L, http.StatusInternalServerError, err, "exchange failed")
return
Expand Down Expand Up @@ -525,16 +515,9 @@ func (ah *AppleHandler) prepareLoginURL(state, path string) (string, error) {
query.Set("response_mode", ah.conf.ResponseMode)
query.Set("client_id", ah.conf.ClientID)
query.Set("scope", scopesList)
query.Set("redirect_uri", ah.makeRedirURL(path))
query.Set("redirect_uri", makeRedirectURL(ah.URL, path))
authURL.RawQuery = query.Encode()

return authURL.String(), nil

}

func (ah AppleHandler) makeRedirURL(path string) string {
elems := strings.Split(path, "/")
newPath := strings.Join(elems[:len(elems)-1], "/")

return strings.TrimRight(ah.URL, "/") + strings.TrimSuffix(newPath, "/") + urlCallbackSuffix
}
14 changes: 8 additions & 6 deletions v2/provider/apple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ func TestAppleHandlerMakeRedirURL(t *testing.T) {
for i := range cases {
c := cases[i]
ah.URL = c.rootURL
assert.Equal(t, c.out, ah.makeRedirURL(c.route))
assert.Equal(t, c.out, makeRedirectURL(ah.URL, c.route))
}
}

Expand Down Expand Up @@ -356,11 +356,13 @@ func TestAppleHandler_Exchange(t *testing.T) {
err = ah.exchange(context.Background(), "1122334455", "url/callback", &testAppleResponse)
assert.NoError(t, err)
assert.Equal(t, &appleVerificationResponse{
AccessToken: "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
ExpiresIn: 3600,
TokenType: "bearer",
RefreshToken: "IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
IDToken: testResponseToken,
AccessTokenResponse: AccessTokenResponse{
AccessToken: "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
ExpiresIn: 3600,
TokenType: "bearer",
RefreshToken: "IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
},
IDToken: testResponseToken,
}, &testAppleResponse)

testAppleResponse = appleVerificationResponse{} // clear response for next checking
Expand Down
18 changes: 18 additions & 0 deletions v2/provider/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package provider

import "strings"

type AccessTokenResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
Scope []string `json:"scope,omitempty"`
}

func makeRedirectURL(url, path string) string {
elems := strings.Split(path, "/")
newPath := strings.Join(elems[:len(elems)-1], "/")

return strings.TrimRight(url, "/") + strings.TrimSuffix(newPath, "/") + urlCallbackSuffix
}
27 changes: 27 additions & 0 deletions v2/provider/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package provider

import (
"github.com/stretchr/testify/assert"
"testing"
)

func Test_makeRedirectURL(t *testing.T) {
type args struct {
url string
path string
}

tests := []struct {
name string
args args
want string
}{
{name: "", args: args{url: "https://some-site.com", path: "/"}, want: "https://some-site.com/callback"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, makeRedirectURL(tt.args.url, tt.args.path), "makeRedirectURL(%v, %v)", tt.args.url, tt.args.path)
})
}
}
Loading