oidc: github.com/ericchiang/oidc Index | Files | Directories

package oidc

import "github.com/ericchiang/oidc"

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

Package Files

doc.go jwks.go nonce.go oidc.go

Constants

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

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")
)

func Nonce Uses

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.

type IDToken Uses

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 Uses

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

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

type IDTokenVerifier Uses

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

IDTokenVerifier provides verification for ID Tokens.

func (*IDTokenVerifier) Verify Uses

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 Uses

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 Uses

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 Uses

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

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

func (*Provider) Claims Uses

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

Claims returns additional fields returned by the server during discovery.

func (*Provider) Endpoint Uses

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

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

func (*Provider) NewVerifier Uses

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 Uses

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 Uses

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 Uses

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

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

type VerificationOption Uses

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

VerificationOption is an option provided to Provider.NewVerifier.

func VerifyAudience Uses

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 Uses

func VerifyExpiry() VerificationOption

VerifyExpiry ensures that an ID Token has not expired.

func VerifyNonce Uses

func VerifyNonce(source NonceSource) VerificationOption

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

Directories

PathSynopsis
oidcproxy

Package oidc imports 14 packages (graph) and is imported by 3 packages. Updated 2018-11-16. Refresh now. Tools for package owners.