Skip to content
This repository has been archived by the owner on Jan 24, 2019. It is now read-only.

Add auth0 integration #567

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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Valid providers are :
* [GitHub](#github-auth-provider)
* [GitLab](#gitlab-auth-provider)
* [LinkedIn](#linkedin-auth-provider)
* [Auth0](#auth0-auth-provider)

The provider can be selected using the `provider` configuration value.

Expand Down Expand Up @@ -133,6 +134,14 @@ For LinkedIn, the registration steps are:
3. Fill in the remaining required fields and Save.
4. Take note of the **Consumer Key / API Key** and **Consumer Secret / Secret Key**

### Auth0 Auth Provider

For Auth0, the registration steps are:

1. Create a new tenant: https://manage.auth0.com/ (this will be the 'auth0-domain' conf)
2. Create a new client (Regular Web Application or Single Page Application)
3. use the client_id and client_secret generated by auth0

### Microsoft Azure AD Provider

For adding an application to the Microsoft Azure AD follow [these steps to add an application](https://azure.microsoft.com/en-us/documentation/articles/active-directory-integrating-applications/).
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func main() {
flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.")
flagSet.String("github-org", "", "restrict logins to members of this organisation")
flagSet.String("github-team", "", "restrict logins to members of this team")
flagSet.String("auth0-domain", "", "the auth0 domain (tenant-name.zone.auth0.com)")
flagSet.Var(&googleGroups, "google-group", "restrict logins to members of this google group (may be given multiple times).")
flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls")
flagSet.String("google-service-account-json", "", "the path to the service account json credentials")
Expand Down
3 changes: 3 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Options struct {
EmailDomains []string `flag:"email-domain" cfg:"email_domains"`
GitHubOrg string `flag:"github-org" cfg:"github_org"`
GitHubTeam string `flag:"github-team" cfg:"github_team"`
Auth0Domain string `flag:"auth0-domain" cfg:"auth0_domain"`
GoogleGroups []string `flag:"google-group" cfg:"google_group"`
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"`
Expand Down Expand Up @@ -263,6 +264,8 @@ func parseProviderInfo(o *Options, msgs []string) []string {
p.Configure(o.AzureTenant)
case *providers.GitHubProvider:
p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
case *providers.Auth0Provider:
p.Configure(o.Auth0Domain)
case *providers.GoogleProvider:
if o.GoogleServiceAccountJSON != "" {
file, err := os.Open(o.GoogleServiceAccountJSON)
Expand Down
90 changes: 90 additions & 0 deletions providers/auth0.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package providers

import (
"errors"
"fmt"
"net/http"
"net/url"

"github.com/bitly/oauth2_proxy/api"
)

type Auth0Provider struct {
*ProviderData
Domain string
}

func NewAuth0Provider(p *ProviderData) *Auth0Provider {
p.ProviderName = "Auth0"
p.Scope = "openid profile email"
return &Auth0Provider{ProviderData: p}
}

func (p *Auth0Provider) Configure(domain string) {
if domain == "" {
panic(fmt.Sprintf("auth0 domain not set"))
}
p.Domain = domain
p.LoginURL = &url.URL{Scheme: "https",
Host: domain,
Path: "/authorize",
}
p.RedeemURL = &url.URL{Scheme: "https",
Host: domain,
Path: "/oauth/token",
}
p.ProfileURL = &url.URL{Scheme: "https",
Host: domain,
Path: "/userinfo",
}
p.ValidateURL = p.ProfileURL
}

func (p *Auth0Provider) GetLoginURL(redirectURI, state string) string {
var a url.URL
a = *p.LoginURL
params, _ := url.ParseQuery(a.RawQuery)
params.Set("redirect_uri", redirectURI)
params.Set("approval_prompt", p.ApprovalPrompt)
params.Add("scope", p.Scope)
params.Set("client_id", p.ClientID)
params.Set("response_type", "code")
params.Add("state", state)
a.RawQuery = params.Encode()
return a.String()
}

func getAuth0Header(access_token string) http.Header {
header := make(http.Header)
header.Set("Accept", "application/json")
header.Set("Authorization", fmt.Sprintf("Bearer %s", access_token))
return header
}

func (p *Auth0Provider) GetEmailAddress(s *SessionState) (string, error) {
if s.AccessToken == "" {
return "", errors.New("missing access token")
}
req, err := http.NewRequest("GET", p.ProfileURL.String(), nil)
if err != nil {
return "", err
}
req.Header = getAuth0Header(s.AccessToken)

type result struct {
Email string
}
var r result
err = api.RequestJson(req, &r)
if err != nil {
return "", err
}
if r.Email == "" {
return "", errors.New("no email")
}
return r.Email, nil
}

func (p *Auth0Provider) ValidateSessionState(s *SessionState) bool {
return validateToken(p, s.AccessToken, getAuth0Header(s.AccessToken))
}
55 changes: 55 additions & 0 deletions providers/auth0_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package providers

import (
"net/url"
"testing"

"github.com/stretchr/testify/assert"
)

func testAuth0Provider() *Auth0Provider {
p := NewAuth0Provider(
&ProviderData{
ApprovalPrompt: "approvalPrompt",
ClientID: "clientID",
LoginURL: &url.URL{},
RedeemURL: &url.URL{},
ProfileURL: &url.URL{},
ValidateURL: &url.URL{},
ProtectedResource: &url.URL{},
})
return p
}

func TestAuth0ProviderDefaultsConfigure(t *testing.T) {
p := testAuth0Provider()
p.Configure("domain.zone.auth0.com")

assert.Equal(t, "Auth0", p.Data().ProviderName)
assert.Equal(t, "domain.zone.auth0.com", p.Domain)
assert.Equal(t, "https://domain.zone.auth0.com/authorize",
p.Data().LoginURL.String())
assert.Equal(t, "https://domain.zone.auth0.com/oauth/token",
p.Data().RedeemURL.String())
assert.Equal(t, "https://domain.zone.auth0.com/userinfo",
p.Data().ProfileURL.String())
assert.Equal(t, "",
p.Data().ProtectedResource.String())
assert.Equal(t, "https://domain.zone.auth0.com/userinfo",
p.Data().ValidateURL.String())
assert.Equal(t, "openid profile email", p.Data().Scope)
}

func TestAuth0GetLoginUrl(t *testing.T) {
p := testAuth0Provider()
p.Configure("domain.zone.auth0.com")
actual := p.GetLoginURL("http://localhost:1234", "state")
assert.Equal(t, "https://domain.zone.auth0.com/authorize?"+
"approval_prompt=approvalPrompt"+
"&client_id=clientID"+
"&redirect_uri=http%3A%2F%2Flocalhost%3A1234"+
"&response_type=code"+
"&scope=openid+profile+email"+
"&state=state",
actual)
}
2 changes: 2 additions & 0 deletions providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ func New(provider string, p *ProviderData) Provider {
return NewGitLabProvider(p)
case "oidc":
return NewOIDCProvider(p)
case "auth0":
return NewAuth0Provider(p)
default:
return NewGoogleProvider(p)
}
Expand Down