109 lines
2.5 KiB
Go
Raw Normal View History

package provider
import (
"context"
"errors"
"github.com/coreos/go-oidc"
"golang.org/x/oauth2"
)
// OIDC provider
type OIDC struct {
OAuthProvider
IssuerURL string `long:"issuer-url" env:"ISSUER_URL" description:"Issuer URL"`
ClientID string `long:"client-id" env:"CLIENT_ID" description:"Client ID"`
ClientSecret string `long:"client-secret" env:"CLIENT_SECRET" description:"Client Secret" json:"-"`
provider *oidc.Provider
verifier *oidc.IDTokenVerifier
}
// Name returns the name of the provider
func (o *OIDC) Name() string {
return "oidc"
}
// Setup performs validation and setup
func (o *OIDC) Setup() error {
// Check parms
if o.IssuerURL == "" || o.ClientID == "" || o.ClientSecret == "" {
return errors.New("providers.oidc.issuer-url, providers.oidc.client-id, providers.oidc.client-secret must be set")
}
var err error
o.ctx = context.Background()
// Try to initiate provider
o.provider, err = oidc.NewProvider(o.ctx, o.IssuerURL)
if err != nil {
return err
}
// Create oauth2 config
o.Config = &oauth2.Config{
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
Endpoint: o.provider.Endpoint(),
// "openid" is a required scope for OpenID Connect flows.
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
// Create OIDC verifier
o.verifier = o.provider.Verifier(&oidc.Config{
ClientID: o.ClientID,
})
return nil
}
// GetLoginURL provides the login url for the given redirect uri and state
func (o *OIDC) GetLoginURL(redirectURI, state string) string {
return o.OAuthGetLoginURL(redirectURI, state)
}
// ExchangeCode exchanges the given redirect uri and code for a token
func (o *OIDC) ExchangeCode(redirectURI, code string) (string, error) {
token, err := o.OAuthExchangeCode(redirectURI, code)
if err != nil {
return "", err
}
// Extract ID token
rawIDToken, ok := token.Extra("id_token").(string)
if !ok {
return "", errors.New("Missing id_token")
}
return rawIDToken, nil
}
// GetUser uses the given token and returns a complete provider.User object
func (o *OIDC) GetUser(token string) (User, error) {
var user User
// Parse & Verify ID Token
idToken, err := o.verifier.Verify(o.ctx, token)
if err != nil {
return user, err
}
// Extract custom claims
var claims struct {
ID string `json:"sub"`
Email string `json:"email"`
Verified bool `json:"email_verified"`
}
if err := idToken.Claims(&claims); err != nil {
return user, err
}
user.ID = claims.ID
user.Email = claims.Email
user.Verified = claims.Verified
return user, nil
}