oidc

package module
v0.0.0-...-11f6293 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2016 License: Apache-2.0 Imports: 14 Imported by: 7

README

OpenID Connect client support for Go

GoDoc

This package implements OpenID Connect client logic for the golang.org/x/oauth2 package.

provider, err := oidc.NewProvider(ctx, "https://accounts.example.com")
if err != nil {
	return err
}

// Configure an OpenID Connect aware OAuth2 client.
oauth2Config := oauth2.Config{
	ClientID:     clientID,
	ClientSecret: clientSecret,
	RedirectURL:  redirectURL,
	Endpoint:     provider.Endpoint(),
	Scopes:       []string{oidc.ScopeOpenID, "profile", "email"},
}

OAuth2 redirects are unchanged.

func handleRedirect(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
})

For callbacks the provider can be used to query for user information such as email.

func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
	// Verify state...

	oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
	if err != nil {
		http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
		return
	}

	userinfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token))
	if err != nil {
		http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// ...
})

Or the provider can be used to verify and inspect the OpenID Connect ID Token in the token response.

verifier := provider.NewVerifier(ctx)

The verifier itself can be constructed with addition checks, such as verifing a token was issued for a specific client or hasn't expired.

verifier := provier.NewVerifier(ctx, oidc.VerifyAudience(clientID), oidc.VerifyExpiry())

The returned verifier can be used to ensure the ID Token (a JWT) is signed by the provider.

func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
	// Verify state...

	oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
	if err != nil {
		http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Extract the ID Token from oauth2 token.
	rawIDToken, ok := oauth2Token.Extra("id_token").(string)
	if !ok {
		http.Error(w, "No ID Token found", http.StatusInternalServerError)
		return
	}

	// Verify that the ID Token is signed by the provider.
	idToken, err := verifier.Verify(rawIDToken)
	if err != nil {
		http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Unmarshal ID Token for expected custom claims.
	var claims struct {
		Email         string `json:"email"`
		EmailVerified bool   `json:"email_verified"`
	}
	if err := idToken.Claims(&claims); err != nil {
		http.Error(w, "Failed to unmarshal ID Token claims: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// ...
})

Documentation

Overview

Package oidc implements OpenID Connect client logic for the golang.org/x/oauth2 package.

provider, err := oidc.NewProvider(ctx, "https://accounts.example.com")
if err != nil {
	return err
}

// Configure an OpenID Connect aware OAuth2 client.
oauth2Config := oauth2.Config{
	ClientID:     clientID,
	ClientSecret: clientSecret,
	RedirectURL:  redirectURL,
	Endpoint:     provider.Endpoint(),
	Scopes:       []string{oidc.ScopeOpenID, "profile", "email"},
}

OAuth2 redirects are unchanged.

func handleRedirect(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
})

For callbacks the provider can be used to query for user information such as email.

func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
	// Verify state...

	oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
	if err != nil {
		http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
		return
	}

	userinfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token))
	if err != nil {
		http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// ...
})

The provider also has the ability to verify ID Tokens.

verifier := provider.NewVerifier(ctx)

The returned verifier can be used to perform basic validation on ID Token issued by the provider, including verifying the JWT signature. It then returns the payload.

func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
	// Verify state...

	oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
	if err != nil {
		http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Extract the ID Token from oauth2 token.
	rawIDToken, ok := oauth2Token.Extra("id_token").(string)
	if !ok {
		http.Error(w, "No ID Token found", http.StatusInternalServerError)
		return
	}

	// Verify that the ID Token is signed by the provider.
	idToken, err := verifier.Verify(rawIDToken)
	if err != nil {
		http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Unmarshal ID Token for expected custom claims.
	var claims struct {
		Email         string `json:"email"`
		EmailVerified bool   `json:"email_verified"`
	}
	if err := idToken.Claims(&claims); err != nil {
		http.Error(w, "Failed to unmarshal ID Token custom claims: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// ...
})

ID Token nonces are supported.

First, provide a nonce source for nonce validation. This will then be used to wrap the existing provider ID Token verifier.

// A verifier which boths verifies the ID Token signature and nonce.
nonceEnabledVerifier := provider.NewVerifier(ctx, oidc.VerifyNonce(nonceSource))

For the redirect provide a nonce auth code option. This will be placed as a URL parameter during the client redirect.

func handleRedirect(w http.ResponseWriter, r *http.Request) {
	nonce, err := newNonce()
	if err != nil {
		// ...
	}
	// Provide a nonce for the OpenID Connect ID Token.
	http.Redirect(w, r, oauth2Config.AuthCodeURL(state, oidc.Nonce(nonce)), http.StatusFound)
})

The nonce enabled verifier can then be used to verify the nonce while unpacking the ID Token.

func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
	// Verify state...

	oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
	if err != nil {
		http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Extract the ID Token from oauth2 token.
	rawIDToken, ok := oauth2Token.Extra("id_token").(string)
	if !ok {
		http.Error(w, "No ID Token found", http.StatusInternalServerError)
		return
	}

	// Verify that the ID Token is signed by the provider and verify the nonce.
	idToken, err := nonceEnabledVerifier.Verify(rawIDToken)
	if err != nil {
		http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// Continue as above...
})

This package uses contexts to derive HTTP clients in the same way as the oauth2 package. To configure a custom client, use the oauth2 packages HTTPClient context key when constructing the context.

myClient := &http.Client{}

myCtx := context.WithValue(parentCtx, oauth2.HTTPClient, myClient)

// NewProvider will use myClient to make the request.
provider, err := oidc.NewProvider(myCtx, "https://accounts.example.com")

Index

Constants

View Source
const (
	// ScopeOpenID is the mandatory scope for all OpenID Connect OAuth2 requests.
	ScopeOpenID = "openid"

	// ScopeOfflineAccess is an optional scope defined by OpenID Connect for requesting
	// OAuth2 refresh tokens.
	//
	// Support for this scope differs between OpenID Connect providers. For instance
	// Google rejects it, favoring appending "access_type=offline" as part of the
	// authorization request instead.
	//
	// See: https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
	ScopeOfflineAccess = "offline_access"
)

Variables

View Source
var (
	// ErrTokenExpired indicates that a token parsed by a verifier has expired.
	ErrTokenExpired = errors.New("oidc: ID Token expired")
	// ErrNotSupported indicates that the requested optional OpenID Connect endpoint is not supported by the provider.
	ErrNotSupported = errors.New("oidc: endpoint not supported")
)

Functions

func Nonce

func Nonce(nonce string) oauth2.AuthCodeOption

Nonce returns an auth code option which requires the ID Token created by the OpenID Connect provider to contain the specified nonce.

Types

type IDToken

type IDToken struct {
	// The URL of the server which issued this token. This will always be the same
	// as the URL used for initial discovery.
	Issuer string

	// The client, or set of clients, that this token is issued for.
	Audience []string

	// A unique string which identifies the end user.
	Subject string

	IssuedAt time.Time
	Expiry   time.Time
	Nonce    string
	// contains filtered or unexported fields
}

IDToken is an OpenID Connect extension that provides a predictable representation of an authorization event.

The ID Token only holds fields OpenID Connect requires. To access additional claims returned by the server, use the Claims method.

idToken, err := idTokenVerifier.Verify(rawIDToken)
if err != nil {
	// handle error
}
var claims struct {
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
}
if err := idToken.Claims(&claims); err != nil {
	// handle error
}

func (*IDToken) Claims

func (i *IDToken) Claims(v interface{}) error

Claims unmarshals the raw JSON payload of the ID Token into a provided struct.

type IDTokenVerifier

type IDTokenVerifier struct {
	// contains filtered or unexported fields
}

IDTokenVerifier provides verification for ID Tokens.

func (*IDTokenVerifier) Verify

func (v *IDTokenVerifier) Verify(rawIDToken string) (*IDToken, error)

Verify parse the raw ID Token, verifies it's been signed by the provider, preforms additional verification, and returns the claims.

type NonceSource

type NonceSource interface {
	ClaimNonce(nonce string) error
}

NonceSource represents a source which can verify a nonce is valid and has not been claimed before.

type Provider

type Provider struct {
	Issuer      string `json:"issuer"`
	AuthURL     string `json:"authorization_endpoint"`
	TokenURL    string `json:"token_endpoint"`
	JWKSURL     string `json:"jwks_uri"`
	UserInfoURL string `json:"userinfo_endpoint"`
	// contains filtered or unexported fields
}

Provider contains the subset of the OpenID Connect provider metadata needed to request and verify ID Tokens.

func NewProvider

func NewProvider(ctx context.Context, issuer string) (*Provider, error)

NewProvider uses the OpenID Connect disovery mechanism to construct a Provider.

func (*Provider) Claims

func (p *Provider) Claims(v interface{}) error

Claims returns additional fields returned by the server during discovery.

func (*Provider) Endpoint

func (p *Provider) Endpoint() oauth2.Endpoint

Endpoint returns the OAuth2 auth and token endpoints for the given provider.

func (*Provider) NewVerifier

func (p *Provider) NewVerifier(ctx context.Context, options ...VerificationOption) *IDTokenVerifier

NewVerifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs.

The verifier queries the provider to update keys when a signature cannot be verified by the set of keys cached from the previous request.

func (*Provider) UserInfo

func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) (*UserInfo, error)

UserInfo uses the token source to query the provider's user info endpoint.

type UserInfo

type UserInfo struct {
	Subject       string `json:"sub"`
	Profile       string `json:"profile"`
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
	// contains filtered or unexported fields
}

UserInfo represents the OpenID Connect userinfo claims.

func (*UserInfo) Claims

func (u *UserInfo) Claims(v interface{}) error

Claims unmarshals the raw JSON object claims into the provided object.

type VerificationOption

type VerificationOption interface {
	// contains filtered or unexported methods
}

VerificationOption is an option provided to Provider.NewVerifier.

func VerifyAudience

func VerifyAudience(clientID string) VerificationOption

VerifyAudience ensures that an ID Token was issued for the specific client.

Note that a verified token may be valid for other clients, as OpenID Connect allows a token to have multiple audiences.

func VerifyExpiry

func VerifyExpiry() VerificationOption

VerifyExpiry ensures that an ID Token has not expired.

func VerifyNonce

func VerifyNonce(source NonceSource) VerificationOption

VerifyNonce ensures that the ID Token contains a nonce which can be claimed by the nonce source.

Directories

Path Synopsis
examples
idtoken
This is an example application to demonstrate parsing an ID Token.
This is an example application to demonstrate parsing an ID Token.
nonce
This is an example application to demonstrate verifying an ID Token with a nonce.
This is an example application to demonstrate verifying an ID Token with a nonce.
userinfo
This is an example application to demonstrate querying the user info endpoint.
This is an example application to demonstrate querying the user info endpoint.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL