Documentation ¶
Overview ¶
oidc is a package for writing clients that integrate with OIDC Providers using OIDC flows.
Primary types provided by the package:
* Request: represents one OIDC authentication flow for a user. It contains the data needed to uniquely represent that one-time flow across the multiple interactions needed to complete the OIDC flow the user is attempting. All Requests contain an expiration for the user's OIDC flow. Optionally, Requests may contain overrides of configured provider defaults for audiences, scopes and a redirect URL.
* Token: represents an OIDC id_token, as well as an Oauth2 access_token and refresh_token (including the access_token expiry)
* Config: provides the configuration for OIDC provider used by a relying party (for example: client ID/Secret, redirectURL, supported signing algorithms, additional scopes requested, etc)
* Provider: provides integration with a provider. The provider provides capabilities like: generating an auth URL, exchanging codes for tokens, verifying tokens, making user info requests, etc.
The oidc.callback package ¶
The callback package includes handlers (http.HandlerFunc) which can be used for the callback leg an OIDC flow. Callback handlers for both the authorization code flow (with optional PKCE) and the implicit flow are provided.
Example apps ¶
Complete concise example solutions:
* OIDC authentication CLI: https://github.com/hashicorp/cap/tree/main/oidc/examples/cli/
* OIDC authentication SPA: https://github.com/hashicorp/cap/tree/main/oidc/examples/spa/
Example ¶
package main import ( "context" "encoding/json" "fmt" "net/http" "time" "github.com/hashicorp/cap/oidc" ) func main() { ctx := context.Background() // Create a new Config pc, err := oidc.NewConfig( "http://your-issuer.com/", "your_client_id", "your_client_secret", []oidc.Alg{oidc.RS256}, []string{"http://your_redirect_url"}, ) if err != nil { // handle error } // Create a provider p, err := oidc.NewProvider(pc) if err != nil { // handle error } defer p.Done() // Create a Request for a user's authentication attempt that will use the // authorization code flow. (See NewRequest(...) using the WithPKCE and // WithImplicit options for creating a Request that uses those flows.) oidcRequest, err := oidc.NewRequest(2*time.Minute, "http://your_redirect_url/callback") if err != nil { // handle error } // Create an auth URL authURL, err := p.AuthURL(ctx, oidcRequest) if err != nil { // handle error } fmt.Println("open url to kick-off authentication: ", authURL) // Create a http.Handler for OIDC authentication response redirects callbackHandler := func(w http.ResponseWriter, r *http.Request) { // Exchange a successful authentication's authorization code and // authorization state (received in a callback) for a verified Token. t, err := p.Exchange(ctx, oidcRequest, r.FormValue("state"), r.FormValue("code")) if err != nil { // handle error } var claims map[string]interface{} if err := t.IDToken().Claims(&claims); err != nil { // handle error } // Get the user's claims via the provider's UserInfo endpoint var infoClaims map[string]interface{} err = p.UserInfo(ctx, t.StaticTokenSource(), claims["sub"].(string), &infoClaims) if err != nil { // handle error } resp := struct { IDTokenClaims map[string]interface{} UserInfoClaims map[string]interface{} }{claims, infoClaims} enc := json.NewEncoder(w) if err := enc.Encode(resp); err != nil { // handle error } } http.HandleFunc("/callback", callbackHandler) }
Output:
Index ¶
- Constants
- Variables
- func ApplyOpts(opts interface{}, opt ...Option)
- func CreateCodeChallenge(v CodeVerifier) (string, error)
- func EncodeCertificates(certs ...*x509.Certificate) (string, error)
- func NewID(opt ...Option) (string, error)
- func TestGenerateCA(t *testing.T, hosts []string) (*x509.Certificate, string)
- func TestGenerateKeys(t *testing.T) (crypto.PublicKey, crypto.PrivateKey)
- func TestSignJWT(t TestingT, key crypto.PrivateKey, alg string, claims interface{}, ...) string
- func UnmarshalClaims(rawToken string, claims interface{}) error
- type AccessToken
- type Alg
- type ChallengeMethod
- type CleanupT
- type ClientSecret
- type CodeVerifier
- type Config
- type DiscoveryInfo
- type Display
- type HelperT
- type IDToken
- type InfofT
- type Option
- func WithACRValues(values ...string) Option
- func WithAudiences(auds ...string) Option
- func WithClaims(json []byte) Option
- func WithDisplay(d Display) Option
- func WithImplicitFlow(args ...interface{}) Option
- func WithMaxAge(seconds uint) Option
- func WithNoTLS() Option
- func WithNonce(n string) Option
- func WithNow(now func() time.Time) Option
- func WithPKCE(v CodeVerifier) Option
- func WithPrefix(prefix string) Option
- func WithPrompts(prompts ...Prompt) Option
- func WithProviderCA(cert string) Option
- func WithProviderConfig(cfg *ProviderConfig) Option
- func WithRoundTripper(rt http.RoundTripper) Option
- func WithScopes(scopes ...string) Option
- func WithState(s string) Option
- func WithTestDefaults(defaults *TestProviderDefaults) Option
- func WithTestHost(host string) Option
- func WithTestPort(port int) Option
- func WithUILocales(locales ...language.Tag) Option
- func WithVerifier(verifier string) Option
- type Prompt
- type Provider
- func (p *Provider) AuthURL(ctx context.Context, oidcRequest Request) (url string, e error)
- func (p *Provider) ConfigHash() (uint64, error)
- func (p *Provider) DiscoveryInfo(ctx context.Context) (*DiscoveryInfo, error)
- func (p *Provider) Done()
- func (p *Provider) Exchange(ctx context.Context, oidcRequest Request, authorizationState string, ...) (*Tk, error)
- func (p *Provider) HTTPClient() (*http.Client, error)
- func (p *Provider) HTTPClientContext(ctx context.Context) (context.Context, error)
- func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource, validSubject string, ...) error
- func (p *Provider) VerifyIDToken(ctx context.Context, t IDToken, oidcRequest Request, opt ...Option) (map[string]interface{}, error)
- type ProviderConfig
- type RefreshToken
- type Req
- func (r *Req) ACRValues() []string
- func (r *Req) Audiences() []string
- func (r *Req) Claims() []byte
- func (r *Req) Display() Display
- func (r *Req) ImplicitFlow() (bool, bool)
- func (r *Req) IsExpired() bool
- func (r *Req) MaxAge() (uint, time.Time)
- func (r *Req) Nonce() string
- func (r *Req) PKCEVerifier() CodeVerifier
- func (r *Req) Prompts() []Prompt
- func (r *Req) RedirectURL() string
- func (r *Req) Scopes() []string
- func (r *Req) State() string
- func (r *Req) UILocales() []language.Tag
- type Request
- type S256Verifier
- type StaticTokenSource
- type TestProvider
- func (p *TestProvider) Addr() string
- func (p *TestProvider) CACert() string
- func (p *TestProvider) ClientCreds() (clientID, clientSecret string)
- func (p *TestProvider) ExpectedSubject() string
- func (p *TestProvider) HTTPClient() *http.Client
- func (p *TestProvider) PKCEVerifier() CodeVerifier
- func (p *TestProvider) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (p *TestProvider) SetAllowedRedirectURIs(uris []string)
- func (p *TestProvider) SetClientCreds(clientID, clientSecret string)
- func (p *TestProvider) SetCustomAudience(customAudiences ...string)
- func (p *TestProvider) SetCustomClaims(customClaims map[string]interface{})
- func (p *TestProvider) SetDisableImplicit(disable bool)
- func (p *TestProvider) SetDisableJWKs(disable bool)
- func (p *TestProvider) SetDisableToken(disable bool)
- func (p *TestProvider) SetDisableUserInfo(disable bool)
- func (p *TestProvider) SetExpectedAuthCode(code string)
- func (p *TestProvider) SetExpectedAuthNonce(nonce string)
- func (p *TestProvider) SetExpectedExpiry(exp time.Duration)
- func (p *TestProvider) SetExpectedState(s string)
- func (p *TestProvider) SetExpectedSubject(sub string)
- func (p *TestProvider) SetInvalidJWKS(invalid bool)
- func (p *TestProvider) SetNowFunc(n func() time.Time)
- func (p *TestProvider) SetOmitAccessTokens(omitAccessTokens bool)
- func (p *TestProvider) SetOmitAuthTimeClaim(omitAuthTime bool)
- func (p *TestProvider) SetOmitIDTokens(omitIDTokens bool)
- func (p *TestProvider) SetPKCEVerifier(verifier CodeVerifier)
- func (p *TestProvider) SetSigningKeys(privKey crypto.PrivateKey, pubKey crypto.PublicKey, alg Alg, KeyID string)
- func (p *TestProvider) SetSubjectInfo(subjectInfo map[string]*TestSubject)
- func (p *TestProvider) SetSupportedScopes(scope ...string)
- func (p *TestProvider) SetUserInfoReply(resp map[string]interface{})
- func (p *TestProvider) SigningKeys() (crypto.PrivateKey, crypto.PublicKey, Alg, string)
- func (p *TestProvider) Stop()
- func (p *TestProvider) SubjectInfo() map[string]*TestSubject
- func (p *TestProvider) SupportedScopes() []string
- func (p *TestProvider) UserInfoReply() map[string]interface{}
- type TestProviderDefaults
- type TestSigningKey
- type TestSubject
- type TestingLogger
- type TestingT
- type Tk
- type Token
Examples ¶
Constants ¶
const DefaultIDLength = 20
DefaultIDLength is the default length for generated IDs, which are used for state and nonce parameters during OIDC flows.
For ID length requirements see: https://tools.ietf.org/html/rfc6749#section-10.10
const RedactedAccessToken = "[REDACTED: access_token]"
RedactedAccessToken is the redacted string or json for an oauth access_token.
const RedactedClientSecret = "[REDACTED: client secret]"
RedactedClientSecret is the redacted string or json for an oauth client secret.
const RedactedIDToken = "[REDACTED: id_token]"
RedactedIDToken is the redacted string or json for an oidc id_token.
const RedactedRefreshToken = "[REDACTED: refresh_token]"
RedactedRefreshToken is the redacted string or json for an oauth refresh_token.
const RequestExpirySkew = 1 * time.Second
RequestExpirySkew defines a time skew when checking a Request's expiration.
const TokenExpirySkew = 10 * time.Second
TokenExpirySkew defines a time skew when checking a Token's expiration.
Variables ¶
var ( ErrInvalidParameter = errors.New("invalid parameter") ErrNilParameter = errors.New("nil parameter") ErrInvalidCACert = errors.New("invalid CA certificate") ErrInvalidIssuer = errors.New("invalid issuer") ErrExpiredRequest = errors.New("request is expired") ErrInvalidResponseState = errors.New("invalid response state") ErrInvalidSignature = errors.New("invalid signature") ErrInvalidSubject = errors.New("invalid subject") ErrInvalidAudience = errors.New("invalid audience") ErrInvalidNonce = errors.New("invalid nonce") ErrInvalidNotBefore = errors.New("invalid not before") ErrExpiredToken = errors.New("token is expired") ErrInvalidJWKs = errors.New("invalid jwks") ErrInvalidIssuedAt = errors.New("invalid issued at (iat)") ErrInvalidAuthorizedParty = errors.New("invalid authorized party (azp)") ErrInvalidAtHash = errors.New("access_token hash does not match value in id_token") ErrInvalidCodeHash = errors.New("authorization code hash does not match value in id_token") ErrTokenNotSigned = errors.New("token is not signed") ErrMalformedToken = errors.New("token malformed") ErrUnsupportedAlg = errors.New("unsupported signing algorithm") ErrIDGeneratorFailed = errors.New("id generation failed") ErrMissingIDToken = errors.New("id_token is missing") ErrMissingAccessToken = errors.New("access_token is missing") ErrIDTokenVerificationFailed = errors.New("id_token verification failed") ErrNotFound = errors.New("not found") ErrLoginFailed = errors.New("login failed") ErrUserInfoFailed = errors.New("user info failed") ErrInvalidFlow = errors.New("invalid OIDC flow") ErrUnsupportedChallengeMethod = errors.New("unsupported PKCE challenge method") ErrExpiredAuthTime = errors.New("expired auth_time") ErrMissingClaim = errors.New("missing required claim") )
Functions ¶
func ApplyOpts ¶
func ApplyOpts(opts interface{}, opt ...Option)
ApplyOpts takes a pointer to the options struct as a set of default options and applies the slice of opts as overrides.
func CreateCodeChallenge ¶
func CreateCodeChallenge(v CodeVerifier) (string, error)
CreateCodeChallenge creates a code challenge from the verifier. Supported ChallengeMethods: S256
func EncodeCertificates ¶
func EncodeCertificates(certs ...*x509.Certificate) (string, error)
EncodeCertificates will encode a number of x509 certificates to PEM. It will help encode certs for use with the WithProviderCA(...) option.
func NewID ¶
NewID generates a ID with an optional prefix. The ID generated is suitable for a Request's State or Nonce. The ID length will be DefaultIDLen, unless an optional prefix is provided which will add the prefix's length + an underscore. The WithPrefix, WithLen options are supported.
For ID length requirements see: https://tools.ietf.org/html/rfc6749#section-10.10
func TestGenerateCA ¶
TestGenerateCA will generate a test x509 CA cert, along with it encoded in a PEM format.
func TestGenerateKeys ¶
TestGenerateKeys will generate a test ECDSA P-256 pub/priv key pair.
func TestSignJWT ¶
func TestSignJWT(t TestingT, key crypto.PrivateKey, alg string, claims interface{}, keyID []byte) string
TestSignJWT will bundle the provided claims into a test signed JWT.
func UnmarshalClaims ¶
UnmarshalClaims will retrieve the claims from the provided raw JWT token.
Types ¶
type AccessToken ¶
type AccessToken string
AccessToken is an oauth access_token.
func (AccessToken) MarshalJSON ¶
func (t AccessToken) MarshalJSON() ([]byte, error)
MarshalJSON will redact the token.
type Alg ¶
type Alg string
Alg represents asymmetric signing algorithms
const ( // JOSE asymmetric signing algorithm values as defined by RFC 7518. // // See: https://tools.ietf.org/html/rfc7518#section-3.1 RS256 Alg = "RS256" // RSASSA-PKCS-v1.5 using SHA-256 RS384 Alg = "RS384" // RSASSA-PKCS-v1.5 using SHA-384 RS512 Alg = "RS512" // RSASSA-PKCS-v1.5 using SHA-512 ES256 Alg = "ES256" // ECDSA using P-256 and SHA-256 ES384 Alg = "ES384" // ECDSA using P-384 and SHA-384 ES512 Alg = "ES512" // ECDSA using P-521 and SHA-512 PS256 Alg = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256 PS384 Alg = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384 PS512 Alg = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512 EdDSA Alg = "EdDSA" )
type ChallengeMethod ¶
type ChallengeMethod string
ChallengeMethod represents PKCE code challenge methods as defined by RFC 7636.
const ( // PKCE code challenge methods as defined by RFC 7636. // // See: https://tools.ietf.org/html/rfc7636#page-9 S256 ChallengeMethod = "S256" // SHA-256 )
type CleanupT ¶
type CleanupT interface{ Cleanup(func()) }
CleanupT defines an single function interface for a testing.Cleanup(func()).
type ClientSecret ¶
type ClientSecret string
ClientSecret is an oauth client Secret.
func (ClientSecret) MarshalJSON ¶
func (t ClientSecret) MarshalJSON() ([]byte, error)
MarshalJSON will redact the client secret.
func (ClientSecret) String ¶
func (t ClientSecret) String() string
String will redact the client secret.
type CodeVerifier ¶
type CodeVerifier interface { // Verifier returns the code verifier (see: // https://tools.ietf.org/html/rfc7636#section-4.1) Verifier() string // Challenge returns the code verifier's code challenge (see: // https://tools.ietf.org/html/rfc7636#section-4.2) Challenge() string // Method returns the code verifier's challenge method (see // https://tools.ietf.org/html/rfc7636#section-4.2) Method() ChallengeMethod // Copy returns a copy of the verifier Copy() CodeVerifier }
CodeVerifier represents an OAuth PKCE code verifier.
type Config ¶
type Config struct { // ClientID is the relying party ID. ClientID string // ClientSecret is the relying party secret. This may be empty if you only // intend to use the provider with the authorization Code with PKCE or the // implicit flows. ClientSecret ClientSecret // Scopes is a list of default oidc scopes to request of the provider. The // required "oidc" scope is requested by default, and does not need to be // part of this optional list. If a Request has scopes, they will override // this configured list for a specific authentication attempt. Scopes []string // Issuer is a case-sensitive URL string using the https scheme that // contains scheme, host, and optionally, port number and path components // and no query or fragment components. // See the Issuer Identifier spec: https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier // See the OIDC connect discovery spec: https://openid.net/specs/openid-connect-discovery-1_0.html#IdentifierNormalization // See the id_token spec: https://tools.ietf.org/html/rfc7519#section-4.1.1 Issuer string // SupportedSigningAlgs is a list of supported signing algorithms. List of // currently supported algs: RS256, RS384, RS512, ES256, ES384, ES512, // PS256, PS384, PS512 // // The list can be used to limit the supported algorithms when verifying // id_token signatures, an id_token's at_hash claim against an // access_token, etc. SupportedSigningAlgs []Alg // AllowedRedirectURLs is a list of allowed URLs for the provider to // redirect to after a user authenticates. If AllowedRedirects is empty, // the package will not check the Request.RedirectURL() to see if it's // allowed, and the check will be left to the OIDC provider's /authorize // endpoint. AllowedRedirectURLs []string // Audiences is an optional default list of case-sensitive strings to use when // verifying an id_token's "aud" claim (which is also a list) If provided, // the audiences of an id_token must match one of the configured audiences. // If a Request has audiences, they will override this configured list for a // specific authentication attempt. Audiences []string // ProviderCA is an optional CA certs (PEM encoded) to use when sending // requests to the provider. If you have a list of *x509.Certificates, then // see EncodeCertificates(...) to PEM encode them. Note: specifying both // ProviderCA and RoundTripper is an error. ProviderCA string // RoundTripper is an optional http.RoundTripper to use when sending requests // to the provider. Note: specifying both ProviderCA and RoundTripper is an // error. RoundTripper http.RoundTripper // NowFunc is a time func that returns the current time. NowFunc func() time.Time `json:"-"` // ProviderConfig is an optional ProviderConfig that supports creating a // provider that doesn't support OIDC discovery. It's probably better to use // NewProvider(...) with discovery whenever possible. ProviderConfig *ProviderConfig }
Config represents the configuration for an OIDC provider used by a relying party.
func NewConfig ¶
func NewConfig(issuer string, clientID string, clientSecret ClientSecret, supported []Alg, allowedRedirectURLs []string, opt ...Option) (*Config, error)
NewConfig composes a new config for a provider.
The "oidc" scope will always be added to the new configuration's Scopes, regardless of what additional scopes are requested via the WithScopes option and duplicate scopes are allowed.
Supported options: WithProviderCA, WithScopes, WithAudiences, WithNow, WithProviderConfig
Example ¶
package main import ( "fmt" "github.com/hashicorp/cap/oidc" ) func main() { // Create a new Config pc, err := oidc.NewConfig( "http://your_issuer/", "your_client_id", "your_client_secret", []oidc.Alg{oidc.RS256}, []string{"http://your_redirect_url/callback"}, ) if err != nil { // handle error } fmt.Println(pc) }
Output: &{your_client_id [REDACTED: client secret] [openid] http://your_issuer/ [RS256] [http://your_redirect_url/callback] [] <nil> <nil> <nil>}
func (*Config) Hash ¶
Hash will produce a hash value for the Config, which is suitable to use for comparing two configurations for equality.
func (*Config) Validate ¶
Validate the provider configuration. Among other validations, it verifies the issuer is not empty, but it doesn't verify the Issuer is discoverable via an http request. SupportedSigningAlgs are validated against the list of currently supported algs: RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384, PS512
type DiscoveryInfo ¶
type DiscoveryInfo struct { // Issuer (REQUIRED): Issuer is a case-sensitive URL string using the https // scheme that contains scheme, host, and optionally, port number and path // components and no query or fragment components. Issuer string `json:"issuer"` // AuthURL (REQUIRED): URL of the OP's OAuth 2.0 Authorization Endpoint AuthURL string `json:"authorization_endpoint"` // TokenURL (REQUIRED): URL of the OP's OAuth 2.0 Token Endpoint TokenURL string `json:"token_endpoint"` // UserInfoURL (OPTIONAL, omitempty): URL of the OP's UserInfo Endpoint UserInfoURL string `json:"userinfo_endpoint,omitempty"` // JWKSURL (REQUIRED): URL of the OP's JSON Web Key Set [JWK] document JWKSURL string `json:"jwks_uri"` // ScopesSupported (RECOMMENDED, omitempty): scope values that this server // supports. ScopesSupported []string `json:"scopes_supported,omitempty"` // GrantTypesSupported (OPTIONAL, omitempty): a list of the OAuth 2.0 // Grant Type values that this OP supports. Dynamic OpenID Providers // MUST support the authorization_code and implicit Grant Type values // and MAY support other Grant Types. If omitted, the default value is // ["authorization_code", "implicit"]. GrantTypesSupported []string `json:"grant_types_supported,omitempty"` // IdTokenSigningAlgsSupported (REQUIRED): a list of the JWS signing // algorithms (alg values) supported by the OP for the ID Token to // encode the Claims in a JWT [JWT]. The algorithm RS256 MUST be included. IdTokenSigningAlgsSupported []string `json:"id_token_signing_alg_values_supported"` // DisplayValuesSupported (OPTIONAL, omitempty): a list of the display parameter // values that the OpenID Provider supports. DisplayValuesSupported []string `json:"display_values_supported,omitempty"` // UILocalesSupported (OPTIONAL, omitempty): Languages and scripts supported // for the user interface UILocalesSupported []string `json:"ui_locales_supported,omitempty"` // ClaimsParameterSupported (OPTIONAL, omitempty): Boolean value specifying whether // the OP supports use of the claims parameter, with true indicating support. ClaimsParameterSupported bool `json:"claims_parameter_supported,omitempty"` // ClaimsSupported (RECOMMENDED, omitempty): a list of the Claim Names of // the Claims that the OpenID Provider MAY be able to supply values for. // Note that for privacy or other reasons, this might not be an exhaustive // list. ClaimsSupported []string `json:"claims_supported,omitempty"` // AcrValuesSupported (OPTIONAL, omitempty): a list of the Authentication // Context Class References that this OP supports AcrValuesSupported []string `json:"acr_values_supported,omitempty"` }
DiscoveryInfo is the Provider's configuration info which is published to a well-known discoverable location (based on the Issuer of the provider).
type Display ¶
type Display string
Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User.
See: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
const ( // Defined the Display values that specifies how the Authorization Server // displays the authentication and consent user interface pages to the End-User. // // See: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest Page Display = "page" Popup Display = "popup" Touch Display = "touch" WAP Display = "wap" )
type HelperT ¶
type HelperT interface{ Helper() }
HelperT defines a single function interface for a testing.Helper()
type IDToken ¶
type IDToken string
IDToken is an oidc id_token. See https://openid.net/specs/openid-connect-core-1_0.html#IDToken.
func (IDToken) MarshalJSON ¶
MarshalJSON will redact the token.
func (IDToken) VerifyAccessToken ¶
func (t IDToken) VerifyAccessToken(accessToken AccessToken) (bool, error)
VerifyAccessToken verifies the at_hash claim of the id_token against the hash of the access_token.
It will return true when it can verify the access_token. It will return false when it's unable to verify the access_token.
It will return an error whenever it's possible to verify the access_token and the verification fails.
Note: while we support signing id_tokens with EdDSA, unfortunately the access_token hash cannot be verified without knowing the key's curve. See: https://bitbucket.org/openid/connect/issues/1125
For more info about verifying access_tokens returned during an OIDC flow see: https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
func (IDToken) VerifyAuthorizationCode ¶
VerifyAuthorizationCode verifies the c_hash claim of the id_token against the hash of the authorization code.
It will return true when it can verify the authorization code. It will return false when it's unable to verify the authorization code.
It will return an error whenever it's possible to verify the authorization code and the verification fails.
Note: while we support signing id_tokens with EdDSA, unfortunately the authorization code hash cannot be verified without knowing the key's curve. See: https://bitbucket.org/openid/connect/issues/1125
For more info about authorization code verification using the id_token's c_hash claim see: https://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken
type InfofT ¶
type InfofT interface {
Infof(format string, args ...interface{})
}
InfofT defines a single function interface for a Info(format string, args ...interface{})
type Option ¶
type Option func(interface{})
Option defines a common functional options type which can be used in a variadic parameter pattern.
func WithACRValues ¶
WithACRValues optionally specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference.
NOTE: Requested acr_values are not verified by the Provider.Exchange(...) or Provider.VerifyIDToken() functions, since the request/return values are determined by the provider's implementation. You'll need to verify the claims returned yourself based on values provided by you OIDC Provider's documentation.
Option is valid for: Request
https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
func WithAudiences ¶
WithAudiences provides an optional list of audiences.
Valid for: Config and Request
func WithClaims ¶
WithClaims optionally requests that specific claims be returned using the claims parameter.
Option is valid for: Request
https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
func WithDisplay ¶
WithDisplay optionally specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User.
Option is valid for: Request
https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
func WithImplicitFlow ¶
func WithImplicitFlow(args ...interface{}) Option
WithImplicitFlow provides an option to use an OIDC implicit flow with form post. It should be noted that if your OIDC provider supports PKCE, then use it over the implicit flow. Getting only an id_token is the default, and optionally passing a true bool will request an access_token as well during the flow. You cannot use WithImplicit and WithPKCE together. It is recommend to not request access_tokens during the implicit flow. If you need an access_token, then use the authorization code flows.
Option is valid for: Request
See: https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth See: https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
func WithMaxAge ¶
WithMaxAge provides an optional maximum authentication age, which is the allowable elapsed time in seconds since the last time the user was actively authenticated by the provider. When a max age is specified, the provider must include a auth_time claim in the returned id_token. This makes it preferable to prompt=login, where you have no way to verify when an authentication took place.
Option is valid for: Request
See: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
func WithNoTLS ¶
func WithNoTLS() Option
WithNoTLS provides the option to not use TLS for the test provider.
Valid for: StartTestProvider(...)
func WithNonce ¶
WithNonce optionally specifies a value to use for the request's nonce. The nonce value is a case sensitive string. A nonce allows you to associate a Client session with an ID Token, because the Provider must include a nonce claim in the ID Token with the claim value being the nonce value sent in the Authentication Request
Typically, a nonce is a random string generated for you when you create a new Request. This option allows you to override that auto-generated value with a specific value of your own choosing.
The primary reason for a nonce is to mitigate replay attacks by using a unique and non-guessable value associated with the Client session which the Provider will add to the ID Token as the nonce claim value.
A nonce should be at least 20 chars long (see: https://tools.ietf.org/html/rfc6749#section-10.10).
See NewID(...) for a function that generates a sufficiently random string and supports the WithPrefix(...) option, which can be used prefix your custom nonce payload.
Neither a max or min length is enforced when you use the WithNonce option.
Option is valid for: Request
func WithNow ¶
WithNow provides an optional func for determining what the current time it is.
Valid for: Config, Tk and Request
func WithPKCE ¶
func WithPKCE(v CodeVerifier) Option
WithPKCE provides an option to use a CodeVerifier with the authorization code flow with PKCE. You cannot use WithImplicit and WithPKCE together.
Option is valid for: Request
func WithPrefix ¶
WithPrefix provides an optional prefix for an new ID. When this options is provided, NewID will prepend the prefix and an underscore to the new identifier.
Valid for: ID
func WithPrompts ¶
WithPrompts provides an optional list of values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
See MaxAge() if wish to specify an allowable elapsed time in seconds since the last time the End-User was actively authenticated by the OP.
Option is valid for: Request
https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
func WithProviderCA ¶
WithProviderCA provides optional CA certs (PEM encoded) for the provider's config. These certs will be used when making http requests to the provider.
Valid for: Config
See EncodeCertificates(...) to PEM encode a number of certs.
Note: specifying both WithProviderCA and WithRoundTripper is a error.
func WithProviderConfig ¶ added in v0.3.0
func WithProviderConfig(cfg *ProviderConfig) Option
WithProviderConfig provides an optional ProviderConfig which supports creating a provider that doesn't support OIDC discovery. It's probably better to use NewProvider(...) with discovery whenever possible.
Example ¶
package main import ( "encoding/json" "fmt" "github.com/hashicorp/cap/oidc" ) func main() { // Create a new Config pc, err := oidc.NewConfig( "https://your_issuer/", "your_client_id", "your_client_secret", []oidc.Alg{oidc.RS256}, []string{"https://your_redirect_url/callback"}, oidc.WithProviderConfig(&oidc.ProviderConfig{ AuthURL: "https://your_issuer/authorize", TokenURL: "https://your_issuer/token", JWKSURL: "https://your_issuer/.well-known/jwks.json", UserInfoURL: "https://your_issuer/userinfo", }), ) if err != nil { // handle error } val, _ := json.Marshal(pc) fmt.Println(string(val)) }
Output: {"ClientID":"your_client_id","ClientSecret":"[REDACTED: client secret]","Scopes":["openid"],"Issuer":"https://your_issuer/","SupportedSigningAlgs":["RS256"],"AllowedRedirectURLs":["https://your_redirect_url/callback"],"Audiences":null,"ProviderCA":"","RoundTripper":null,"ProviderConfig":{"AuthURL":"https://your_issuer/authorize","TokenURL":"https://your_issuer/token","UserInfoURL":"https://your_issuer/userinfo","JWKSURL":"https://your_issuer/.well-known/jwks.json"}}
func WithRoundTripper ¶ added in v0.7.0
func WithRoundTripper(rt http.RoundTripper) Option
WithRoundTripper provides and optional RoundTripper for the provider's config. This RoundTripper will be used when making http requests to the provider. Note: specifying both WithProviderCA and WithRoundTripper is a error.
func WithState ¶
WithState optionally specifies a value to use for the request's state. Typically, state is a random string generated for you when you create a new Request. This option allows you to override that auto-generated value with a specific value of your own choosing.
The primary reason for using the state parameter is to mitigate CSRF attacks by using a unique and non-guessable value associated with each authentication request about to be initiated. That value allows you to prevent the attack by confirming that the value coming from the response matches the one you sent. Since the state parameter is a string, you can encode any other information in it.
Some care must be taken to not use a state which is longer than your OIDC Provider allows. The specification places no limit on the length, but there are many practical limitations placed on the length by browsers, proxies and of course your OIDC provider.
State should be at least 20 chars long (see: https://tools.ietf.org/html/rfc6749#section-10.10).
See NewID(...) for a function that generates a sufficiently random string and supports the WithPrefix(...) option, which can be used prefix your custom state payload.
Neither a max or min length is enforced when you use the WithState option.
Option is valid for: Request
func WithTestDefaults ¶
func WithTestDefaults(defaults *TestProviderDefaults) Option
WithTestDefaults provides an option to provide a set of defaults to StartTestProvider(...) which make it much more composable.
Valid for: StartTestProvider(...)
func WithTestHost ¶
WithTestHost provides an optional address for the test provider.
Valid for: TestProvider.StartTestProvider
func WithTestPort ¶
WithTestPort provides an optional port for the test provider. -1 causes an unstarted server with a random port. 0 causes a started server with a random port. Any other value returns a started server on that port.
Valid for: TestProvider.StartTestProvider
func WithUILocales ¶
WithUILocales optionally specifies End-User's preferred languages via language Tags, ordered by preference.
Option is valid for: Request
https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
func WithVerifier ¶ added in v0.8.0
WithVerifier provides an optional verifier for the code verifier. When this option is provided, NewCodeVerifier will use the provided verifier. Note the verifier must use the base62 character set. See: https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
Valid for: NewVerifier
type Prompt ¶
type Prompt string
Prompt is a string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
See: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
const ( // Defined the Prompt values that specifies whether the Authorization Server // prompts the End-User for reauthentication and consent. // // See: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest None Prompt = "none" Login Prompt = "login" Consent Prompt = "consent" SelectAccount Prompt = "select_account" )
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider provides integration with an OIDC provider.
It's primary capabilities include: * Kicking off a user authentication via either the authorization code flow (with optional PKCE) or implicit flow via the URL from p.AuthURL(...) * The authorization code flow (with optional PKCE) by exchanging an auth code for tokens in p.Exchange(...) * Verifying an id_token issued by a provider with p.VerifyIDToken(...) * Retrieving a user's OAuth claims with p.UserInfo(...)
func NewProvider ¶
NewProvider creates and initializes a Provider. Intializing the provider, includes making an http request to the provider's issuer.
See Provider.Done() which must be called to release provider resources.
Example ¶
package main import ( "github.com/hashicorp/cap/oidc" ) func main() { // Create a new Config pc, err := oidc.NewConfig( "http://your_issuer/", "your_client_id", "your_client_secret", []oidc.Alg{oidc.RS256}, []string{"http://your_redirect_url/callback"}, ) if err != nil { // handle error } // Create a provider p, err := oidc.NewProvider(pc) if err != nil { // handle error } defer p.Done() }
Output:
func (*Provider) AuthURL ¶
AuthURL will generate a URL the caller can use to kick off an OIDC authorization code (with optional PKCE) or an implicit flow with an IdP.
See NewRequest() to create an oidc flow Request with a valid state and Nonce that will uniquely identify the user's authentication attempt throughout the flow.
Example ¶
package main import ( "context" "fmt" "time" "github.com/hashicorp/cap/oidc" ) func main() { ctx := context.Background() // Create a new Config pc, err := oidc.NewConfig( "http://your_issuer/", "your_client_id", "your_client_secret", []oidc.Alg{oidc.RS256}, []string{"http://your_redirect_url/callback"}, ) if err != nil { // handle error } // Create a provider p, err := oidc.NewProvider(pc) if err != nil { // handle error } defer p.Done() // Create a Request for a user's authentication attempt that will use the // authorization code flow. (See NewRequest(...) using the WithPKCE and // WithImplicit options for creating a Request that uses those flows.) oidcRequest, err := oidc.NewRequest(2*time.Minute, "http://your_redirect_url/callback") if err != nil { // handle error } // Create an auth URL authURL, err := p.AuthURL(ctx, oidcRequest) if err != nil { // handle error } fmt.Println("open url to kick-off authentication: ", authURL) }
Output:
func (*Provider) ConfigHash ¶
ConfigHash will produce a hash value for the provider's Config, which is suitable to use for comparing two configurations for equality, which is important if you're caching the providers
func (*Provider) DiscoveryInfo ¶
func (p *Provider) DiscoveryInfo(ctx context.Context) (*DiscoveryInfo, error)
DiscoveryInfo will use the provider's Issuer to discover and retrieve its published configuration
See: https://openid.net/specs/openid-connect-discovery-1_0.html
func (*Provider) Done ¶
func (p *Provider) Done()
Done with the provider's background resources and must be called for every Provider created
func (*Provider) Exchange ¶
func (p *Provider) Exchange(ctx context.Context, oidcRequest Request, authorizationState string, authorizationCode string) (*Tk, error)
Exchange will request a token from the oidc token endpoint, using the authorizationCode and authorizationState it received in an earlier successful oidc authentication response.
Exchange will use PKCE when the user's oidc Request specifies its use.
It will also validate the authorizationState it receives against the existing Request for the user's oidc authentication flow.
On success, the Token returned will include an IDToken and may include an AccessToken and RefreshToken.
Any tokens returned will have been verified. See: Provider.VerifyIDToken for info about id_token verification.
When present, the id_token at_hash claim is verified against the access_token. (see: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowTokenValidation)
The id_token c_hash claim is verified when present.
Example ¶
package main import ( "context" "encoding/json" "fmt" "net/http" "time" "github.com/hashicorp/cap/oidc" ) func main() { ctx := context.Background() // Create a new Config pc, err := oidc.NewConfig( "http://your-issuer.com/", "your_client_id", "your_client_secret", []oidc.Alg{oidc.RS256}, []string{"http://your_redirect_url"}, ) if err != nil { // handle error } // Create a provider p, err := oidc.NewProvider(pc) if err != nil { // handle error } defer p.Done() // Create a Request for a user's authentication attempt that will use the // authorization code flow. (See NewRequest(...) using the WithPKCE and // WithImplicit options for creating a Request that uses those flows.) oidcRequest, err := oidc.NewRequest(2*time.Minute, "http://your_redirect_url/callback") if err != nil { // handle error } // Create an auth URL authURL, err := p.AuthURL(ctx, oidcRequest) if err != nil { // handle error } fmt.Println("open url to kick-off authentication: ", authURL) // Create a http.Handler for OIDC authentication response redirects callbackHandler := func(w http.ResponseWriter, r *http.Request) { // Exchange a successful authentication's authorization code and // authorization state (received in a callback) for a verified Token. t, err := p.Exchange(ctx, oidcRequest, r.FormValue("state"), r.FormValue("code")) if err != nil { // handle error } var claims map[string]interface{} if err := t.IDToken().Claims(&claims); err != nil { // handle error } // Get the user's claims via the provider's UserInfo endpoint var infoClaims map[string]interface{} err = p.UserInfo(ctx, t.StaticTokenSource(), claims["sub"].(string), &infoClaims) if err != nil { // handle error } resp := struct { IDTokenClaims map[string]interface{} UserInfoClaims map[string]interface{} }{claims, infoClaims} enc := json.NewEncoder(w) if err := enc.Encode(resp); err != nil { // handle error } } http.HandleFunc("/callback", callbackHandler) }
Output:
func (*Provider) HTTPClient ¶
HTTPClient returns an http.Client for the provider. The returned client uses a pooled transport (so it can reuse connections) that uses the provider's config CA certificate PEM if provided, otherwise it will use the installed system CA chain. This client's idle connections are closed in Provider.Done()
func (*Provider) HTTPClientContext ¶
HTTPClientContext returns a new Context that carries the provider's HTTP client. This method sets the same context key used by the github.com/coreos/go-oidc and golang.org/x/oauth2 packages, so the returned context works for those packages as well.
func (*Provider) UserInfo ¶
func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource, validSubject string, claims interface{}, opt ...Option) error
UserInfo gets the UserInfo claims from the provider using the token produced by the tokenSource. Only JSON user info responses are supported (signed JWT responses are not). The WithAudiences option is supported to specify optional audiences to verify when the aud claim is present in the response.
It verifies: * sub (sub) is required and must match * issuer (iss) - if the iss claim is included in returned claims * audiences (aud) - if the aud claim is included in returned claims and WithAudiences option is provided.
See: https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
Example ¶
package main import ( "context" "encoding/json" "fmt" "net/http" "time" "github.com/hashicorp/cap/oidc" ) func main() { ctx := context.Background() // Create a new Config pc, err := oidc.NewConfig( "http://your-issuer.com/", "your_client_id", "your_client_secret", []oidc.Alg{oidc.RS256}, []string{"http://your_redirect_url"}, ) if err != nil { // handle error } // Create a provider p, err := oidc.NewProvider(pc) if err != nil { // handle error } defer p.Done() // Create a Request for a user's authentication attempt that will use the // authorization code flow. (See NewRequest(...) using the WithPKCE and // WithImplicit options for creating a Request that uses those flows.) oidcRequest, err := oidc.NewRequest(2*time.Minute, "http://your_redirect_url/callback") if err != nil { // handle error } // Create an auth URL authURL, err := p.AuthURL(ctx, oidcRequest) if err != nil { // handle error } fmt.Println("open url to kick-off authentication: ", authURL) // Create a http.Handler for OIDC authentication response redirects callbackHandler := func(w http.ResponseWriter, r *http.Request) { // Exchange a successful authentication's authorization code and // authorization state (received in a callback) for a verified Token. t, err := p.Exchange(ctx, oidcRequest, r.FormValue("state"), r.FormValue("code")) if err != nil { // handle error } var claims map[string]interface{} if err := t.IDToken().Claims(&claims); err != nil { // handle error } // Get the user's claims via the provider's UserInfo endpoint var infoClaims map[string]interface{} err = p.UserInfo(ctx, t.StaticTokenSource(), claims["sub"].(string), &infoClaims) if err != nil { // handle error } resp := struct { IDTokenClaims map[string]interface{} UserInfoClaims map[string]interface{} }{claims, infoClaims} enc := json.NewEncoder(w) if err := enc.Encode(resp); err != nil { // handle error } } http.HandleFunc("/callback", callbackHandler) }
Output:
func (*Provider) VerifyIDToken ¶
func (p *Provider) VerifyIDToken(ctx context.Context, t IDToken, oidcRequest Request, opt ...Option) (map[string]interface{}, error)
VerifyIDToken will verify the inbound IDToken and return its claims.
It verifies: * signature (including if a supported signing algorithm was used) * issuer (iss) * expiration (exp) * issued at (iat) (with a leeway of 1 min) * not before (nbf) (with a leeway of 1 min) * nonce (nonce) * audience (aud) contains all audiences required from the provider's config * when there are multiple audiences (aud), then one of them must equal the client_id * when present, the authorized party (azp) must equal the client id * when there are multiple audiences (aud), then the authorized party (azp) must equal the client id * when there is a single audience (aud) and it is not equal to the client id, then the authorized party (azp) must equal the client id * when max_age was requested, the auth_time claim is verified (with a leeway of 1 min)
See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
type ProviderConfig ¶ added in v0.3.0
type ProviderConfig struct { // AuthURL is the provider's OAuth 2.0 authorization endpoint. AuthURL string // TokenURL is the provider's OAuth2.0 token endpoint. TokenURL string // UserInfoURL is the provider's OpenID UserInfo endpoint. // // See: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo UserInfoURL string // JWKSURL is the provider's OpenID JWKS endpoint (where it publishes the // pub keys. JWKSURL string }
ProviderConfig supports creating a provider that doesn't support OIDC discovery. It's probably better to use NewProvider(...) with discovery whenever possible.
type RefreshToken ¶
type RefreshToken string
RefreshToken is an oauth refresh_token. See https://tools.ietf.org/html/rfc6749#section-1.5.
func (RefreshToken) MarshalJSON ¶
func (t RefreshToken) MarshalJSON() ([]byte, error)
MarshalJSON will redact the token.
type Req ¶
type Req struct {
// contains filtered or unexported fields
}
Req represents the oidc request used for oidc flows and implements the Request interface.
func NewRequest ¶
NewRequest creates a new Request (*Req).
Supports the options: * WithState * WithNonce * WithNow * WithAudiences * WithScopes * WithImplicit * WithPKCE * WithMaxAge * WithPrompts * WithDisplay * WithUILocales * WithClaims
Example ¶
package main import ( "fmt" "time" "github.com/hashicorp/cap/oidc" ) func main() { // Create a Request for a user's authentication attempt that will use the // authorization code flow. (See NewRequest(...) using the WithPKCE and // WithImplicit options for creating a Request that uses those flows.) ttl := 2 * time.Minute oidcRequest, err := oidc.NewRequest(ttl, "http://your_redirect_url/callback") if err != nil { // handle error } fmt.Println(oidcRequest) // Create a Request for a user's authentication attempt that will use the // authorization code flow with PKCE v, err := oidc.NewCodeVerifier() if err != nil { // handle error } oidcRequest, err = oidc.NewRequest(ttl, "http://your_redirect_url/callback", oidc.WithPKCE(v)) if err != nil { // handle error } fmt.Println(oidcRequest) // Create a Request for a user's authentication attempt that will use the // implicit flow. oidcRequest, err = oidc.NewRequest(ttl, "http://your_redirect_url/callback", oidc.WithImplicitFlow()) if err != nil { // handle error } fmt.Println(oidcRequest) // Create a Request for a user's authentication attempt that will use the // authorization code flow and require a auth_time with a max_age of 0 // seconds. ttl = 2 * time.Minute oidcRequest, err = oidc.NewRequest(ttl, "http://your_redirect_url/callback", oidc.WithMaxAge(0)) if err != nil { // handle error } fmt.Println(oidcRequest) }
Output:
func (*Req) ACRValues ¶
ACRValues() implements the Request.ARCValues() interface function and returns a copy of the acr values
func (*Req) Audiences ¶
Audiences implements the Request.Audiences() interface function and returns a copy of the audiences.
func (*Req) Claims ¶
Claims() implements the Request.Claims() interface function and returns a copy of the claims request.
func (*Req) ImplicitFlow ¶
ImplicitFlow indicates whether or not to use the implicit flow. Getting only an id_token for an implicit flow is the default, but at times it's necessary to also request an access_token, so this function and the WithImplicitFlow(...) option allows for those scenarios. Overall, it is recommend to not request access_tokens during the implicit flow. If you need an access_token, then use the authorization code flows and if you can't secure a client secret then use the authorization code flow with PKCE.
The first returned bool represents if the implicit flow has been requested. The second returned bool represents if an access token has been requested during the implicit flow.
func (*Req) MaxAge ¶
MaxAge: when authAfter is not a zero value (authTime.IsZero()) then the id_token's auth_time claim must be after the specified time.
See: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
func (*Req) PKCEVerifier ¶
func (r *Req) PKCEVerifier() CodeVerifier
PKCEVerifier implements the Request.PKCEVerifier() interface function and returns a copy of the CodeVerifier
func (*Req) Prompts ¶
Prompts() implements the Request.Prompts() interface function and returns a copy of the prompts.
func (*Req) RedirectURL ¶
RedirectURL implements the Request.RedirectURL() interface function.
func (*Req) Scopes ¶
Scopes implements the Request.Scopes() interface function and returns a copy of the scopes.
type Request ¶
type Request interface { // State is a unique identifier and an opaque value used to maintain request // between the oidc request and the callback. State cannot equal the Nonce. // See https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest. State() string // Nonce is a unique nonce and a string value used to associate a Client // session with an ID Token, and to mitigate replay attacks. Nonce cannot // equal the ID. // See https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest // and https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes. Nonce() string // IsExpired returns true if the request has expired. Implementations should // support a time skew (perhaps RequestExpirySkew) when checking expiration. IsExpired() bool // Audiences is an specific authentication attempt's list of optional // case-sensitive strings to use when verifying an id_token's "aud" claim // (which is also a list). If provided, the audiences of an id_token must // match one of the configured audiences. If a Request does not have // audiences, then the configured list of default audiences will be used. Audiences() []string // Scopes is a specific authentication attempt's list of optional // scopes to request of the provider. The required "oidc" scope is requested // by default, and does not need to be part of this optional list. If a // Request does not have Scopes, then the configured list of default // requested scopes will be used. Scopes() []string // RedirectURL is a URL where providers will redirect responses to // authentication requests. RedirectURL() string // ImplicitFlow indicates whether or not to use the implicit flow with form // post. Getting only an id_token for an implicit flow should be the // default for implementations, but at times it's necessary to also request // an access_token, so this function and the WithImplicitFlow(...) option // allows for those scenarios. Overall, it is recommend to not request // access_tokens during the implicit flow. If you need an access_token, // then use the authorization code flows and if you can't secure a client // secret then use the authorization code flow with PKCE. // // The first returned bool represents if the implicit flow has been requested. // The second returned bool represents if an access token has been requested // during the implicit flow. // // See: https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth // See: https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html ImplicitFlow() (useImplicitFlow bool, includeAccessToken bool) // PKCEVerifier indicates whether or not to use the authorization code flow // with PKCE. PKCE should be used for any client which cannot secure a // client secret (SPA and native apps) or is susceptible to authorization // code intercept attacks. When supported by your OIDC provider, PKCE should // be used instead of the implicit flow. // // See: https://tools.ietf.org/html/rfc7636 PKCEVerifier() CodeVerifier // MaxAge: when authAfter is not a zero value (authTime.IsZero()) then the // id_token's auth_time claim must be after the specified time. // // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest MaxAge() (seconds uint, authAfter time.Time) // Prompts optionally defines a list of values that specifies whether the // Authorization Server prompts the End-User for reauthentication and // consent. See MaxAge() if wish to specify an allowable elapsed time in // seconds since the last time the End-User was actively authenticated by // the OP. // // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest Prompts() []Prompt // Display optionally specifies how the Authorization Server displays the // authentication and consent user interface pages to the End-User. // // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest Display() Display // UILocales optionally specifies End-User's preferred languages via // language Tags, ordered by preference. // // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest UILocales() []language.Tag // Claims optionally requests that specific claims be returned using // the claims parameter. // // https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter Claims() []byte // ACRValues() optionally specifies the acr values that the Authorization // Server is being requested to use for processing this Authentication // Request, with the values appearing in order of preference. // // NOTE: Requested acr_values are not verified by the Provider.Exchange(...) // or Provider.VerifyIDToken() functions, since the request/return values // are determined by the provider's implementation. You'll need to verify // the claims returned yourself based on values provided by you OIDC // Provider's documentation. // // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest ACRValues() []string }
Request basically represents one OIDC authentication flow for a user. It contains the data needed to uniquely represent that one-time flow across the multiple interactions needed to complete the OIDC flow the user is attempting.
Request() is passed throughout the OIDC interactions to uniquely identify the flow's request. The Request.State() and Request.Nonce() cannot be equal, and will be used during the OIDC flow to prevent CSRF and replay attacks (see the oidc spec for specifics).
Audiences and Scopes are optional overrides of configured provider defaults for specific authentication attempts
type S256Verifier ¶
type S256Verifier struct {
// contains filtered or unexported fields
}
S256Verifier represents an OAuth PKCE code verifier that uses the S256 challenge method. It implements the CodeVerifier interface.
func NewCodeVerifier ¶
func NewCodeVerifier(opt ...Option) (*S256Verifier, error)
NewCodeVerifier creates a new CodeVerifier (*S256Verifier). Supported options: WithVerifier
func (*S256Verifier) Challenge ¶
func (v *S256Verifier) Challenge() string
func (*S256Verifier) Copy ¶
func (v *S256Verifier) Copy() CodeVerifier
Copy returns a copy of the verifier.
func (*S256Verifier) Method ¶
func (v *S256Verifier) Method() ChallengeMethod
func (*S256Verifier) Verifier ¶
func (v *S256Verifier) Verifier() string
type StaticTokenSource ¶
type StaticTokenSource interface {
StaticTokenSource() oauth2.TokenSource
}
StaticTokenSource is a single function interface that defines a method to create a oauth2.TokenSource that always returns the same token. Because the token is never refreshed. A TokenSource can be used to when calling a provider's UserInfo(), among other things.
type TestProvider ¶
type TestProvider struct {
// contains filtered or unexported fields
}
TestProvider is a local http server that supports test provider capabilities which makes writing tests much easier. Much of this TestProvider design/implementation comes from Consul's oauthtest package. A big thanks to the original package's contributors.
It's important to remember that the TestProvider is stateful (see any of its receiver functions that begin with Set*).
Once you've started a TestProvider http server with StartTestProvider(...), the following test endpoints are supported:
GET /.well-known/openid-configuration OIDC Discovery
GET or POST /authorize OIDC authorization supporting both the authorization code flow (with optional PKCE) and the implicit flow with form_post.
POST /token OIDC token
GET /userinfo OAuth UserInfo
GET /.well-known/jwks.json JWKs used to verify issued JWT tokens
Making requests to these endpoints are facilitated by
TestProvider.HTTPClient which returns an http.Client for making requests.
TestProvider.CACert which the pem-encoded CA certificate used by the HTTPS server.
Runtime Configuration:
Issuer: Addr() returns the the current base URL for the test provider's running web server, which can be used as an OIDC Issuer for discovery and is also used for the iss claim when issuing JWTs.
Relying Party ClientID/ClientSecret: SetClientCreds(...) updates the creds and they are empty by default.
Now: SetNowFunc(...) updates the provider's "now" function and time.Now is the default.
Subject: SetExpectedSubject(sub string) configures the expected subject for any JWTs issued by the provider (the default is "alice@example.com")
Subject Passwords: SetSubjectInfo(...) configures a subject/password dictionary. If configured, then an interactive Login form is presented by the /authorize endpoint and the TestProvider becomes an interactive test provider using the provided subject/password dictionary.
Expiry: SetExpectedExpiry(exp time.Duration) updates the expiry and now + 5 * time.Second is the default.
Signing keys: SetSigningKeys(...) updates the keys and a ECDSA P-256 pair of priv/pub keys are the default with a signing algorithm of ES256
Authorization Code: SetExpectedAuthCode(...) updates the auth code required by the /authorize endpoint and the code is empty by default.
Authorization Nonce: SetExpectedAuthNonce(...) updates the nonce required by the /authorize endpoint and the nonce is empty by default.
Allowed RedirectURIs: SetAllowedRedirectURIs(...) updates the allowed redirect URIs and "https://example.com" is the default.
Custom Claims: SetCustomClaims(...) updates custom claims added to JWTs issued and the custom claims are empty by default.
Audiences: SetCustomAudience(...) updates the audience claim of JWTs issued and the ClientID is the default.
Authentication Time (auth_time): SetOmitAuthTimeClaim(...) allows you to turn off/on the inclusion of an auth_time claim in issued JWTs and the claim is included by default.
Issuing id_tokens: SetOmitIDTokens(...) allows you to turn off/on the issuing of id_tokens from the /token endpoint. id_tokens are issued by default.
Issuing access_tokens: SetOmitAccessTokens(...) allows you to turn off/on the issuing of access_tokens from the /token endpoint. access_tokens are issued by default.
Authorization State: SetExpectedState sets the value for the state parameter returned from the /authorized endpoint
Token Responses: SetDisableToken disables the /token endpoint, causing it to return a 401 http status.
Implicit Flow Responses: SetDisableImplicit disables implicit flow responses, causing them to return a 401 http status.
PKCE verifier: SetPKCEVerifier(oidc.CodeVerifier) sets the PKCE code_verifier and PKCEVerifier() returns the current verifier.
UserInfo: SetUserInfoReply sets the UserInfo endpoint response and UserInfoReply() returns the current response.
func StartTestProvider ¶
func StartTestProvider(t TestingT, opt ...Option) *TestProvider
StartTestProvider creates and starts a running TestProvider http server. The WithTestDefaults, WithNoTLS and WithPort options are supported. If the TestingT parameter supports a CleanupT interface, then TestProvider will be shutdown when the test and all it's subtests complete via a registered function with t.Cleanup(...).
func (*TestProvider) Addr ¶
func (p *TestProvider) Addr() string
Addr returns the current base URL for the test provider's running webserver, which can be used as an OIDC issuer for discovery and is also used for the iss claim when issuing JWTs.
func (*TestProvider) CACert ¶
func (p *TestProvider) CACert() string
CACert returns the pem-encoded CA certificate used by the test provider's HTTPS server. If the TestProvider was started the WithNoTLS option, then this will return an empty string
func (*TestProvider) ClientCreds ¶
func (p *TestProvider) ClientCreds() (clientID, clientSecret string)
ClientCreds returns the relying party client information required for the OIDC workflows.
func (*TestProvider) ExpectedSubject ¶
func (p *TestProvider) ExpectedSubject() string
ExpectedSubject returns the subject for any JWTs issued by the provider See: SetExpectedSubject(...) to override the default which is "alice@example.com"
func (*TestProvider) HTTPClient ¶
func (p *TestProvider) HTTPClient() *http.Client
HTTPClient returns an http.Client for the test provider. The returned client uses a pooled transport (so it can reuse connections) that uses the test provider's CA certificate. This client's idle connections are closed in TestProvider.Done()
func (*TestProvider) PKCEVerifier ¶
func (p *TestProvider) PKCEVerifier() CodeVerifier
PKCEVerifier returns the PKCE oidc.CodeVerifier
func (*TestProvider) ServeHTTP ¶
func (p *TestProvider) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP implements the test provider's http.Handler.
func (*TestProvider) SetAllowedRedirectURIs ¶
func (p *TestProvider) SetAllowedRedirectURIs(uris []string)
SetAllowedRedirectURIs allows you to configure the allowed redirect URIs for the OIDC workflow. If not configured a sample of "https://example.com" is used.
func (*TestProvider) SetClientCreds ¶
func (p *TestProvider) SetClientCreds(clientID, clientSecret string)
SetClientCreds is for configuring the relying party client ID and client secret information required for the OIDC workflows.
func (*TestProvider) SetCustomAudience ¶
func (p *TestProvider) SetCustomAudience(customAudiences ...string)
SetCustomAudience configures what audience value to embed in the JWT issued by the OIDC workflow.
func (*TestProvider) SetCustomClaims ¶
func (p *TestProvider) SetCustomClaims(customClaims map[string]interface{})
SetCustomClaims lets you set claims to return in the JWT issued by the OIDC workflow.
func (*TestProvider) SetDisableImplicit ¶
func (p *TestProvider) SetDisableImplicit(disable bool)
SetDisableImplicit makes implicit flow responses return 401
func (*TestProvider) SetDisableJWKs ¶
func (p *TestProvider) SetDisableJWKs(disable bool)
SetDisableJWKs makes the JWKs endpoint return 404
func (*TestProvider) SetDisableToken ¶
func (p *TestProvider) SetDisableToken(disable bool)
SetDisableToken makes the /token endpoint return 401
func (*TestProvider) SetDisableUserInfo ¶
func (p *TestProvider) SetDisableUserInfo(disable bool)
SetDisableUserInfo makes the userinfo endpoint return 404 and omits it from the discovery config.
func (*TestProvider) SetExpectedAuthCode ¶
func (p *TestProvider) SetExpectedAuthCode(code string)
SetExpectedAuthCode configures the auth code to return from /auth and the allowed auth code for /token.
func (*TestProvider) SetExpectedAuthNonce ¶
func (p *TestProvider) SetExpectedAuthNonce(nonce string)
SetExpectedAuthNonce configures the nonce value required for /auth.
func (*TestProvider) SetExpectedExpiry ¶
func (p *TestProvider) SetExpectedExpiry(exp time.Duration)
SetExpectedExpiry is for configuring the expected expiry for any JWTs issued by the provider (the default is 5 seconds)
func (*TestProvider) SetExpectedState ¶
func (p *TestProvider) SetExpectedState(s string)
SetExpectedState sets the value for the state parameter returned from /authorized
func (*TestProvider) SetExpectedSubject ¶
func (p *TestProvider) SetExpectedSubject(sub string)
SetExpectedSubject is for configuring the expected subject for any JWTs issued by the provider (the default is "alice@example.com")
func (*TestProvider) SetInvalidJWKS ¶
func (p *TestProvider) SetInvalidJWKS(invalid bool)
SetInvalidJWKS makes the JWKs endpoint return an invalid response
func (*TestProvider) SetNowFunc ¶
func (p *TestProvider) SetNowFunc(n func() time.Time)
SetNowFunc configures how the test provider will determine the current time. The default is time.Now()
func (*TestProvider) SetOmitAccessTokens ¶
func (p *TestProvider) SetOmitAccessTokens(omitAccessTokens bool)
OmitAccessTokens turn on/off the omitting of access_tokens from the /token endpoint. If set to true, the test provider will not omit (issue) access_tokens from the /token endpoint.
func (*TestProvider) SetOmitAuthTimeClaim ¶
func (p *TestProvider) SetOmitAuthTimeClaim(omitAuthTime bool)
SetOmitAuthTimeClaim turn on/off the omitting of an auth_time claim from id_tokens from the /token endpoint. If set to true, the test provider will not include the auth_time claim in issued id_tokens from the /token endpoint.
func (*TestProvider) SetOmitIDTokens ¶
func (p *TestProvider) SetOmitIDTokens(omitIDTokens bool)
SetOmitIDTokens turn on/off the omitting of id_tokens from the /token endpoint. If set to true, the test provider will not omit (issue) id_tokens from the /token endpoint.
func (*TestProvider) SetPKCEVerifier ¶
func (p *TestProvider) SetPKCEVerifier(verifier CodeVerifier)
SetPKCEVerifier sets the PKCE oidc.CodeVerifier
func (*TestProvider) SetSigningKeys ¶
func (p *TestProvider) SetSigningKeys(privKey crypto.PrivateKey, pubKey crypto.PublicKey, alg Alg, KeyID string)
SetSigningKeys sets the test provider's keys and alg used to sign JWTs.
func (*TestProvider) SetSubjectInfo ¶
func (p *TestProvider) SetSubjectInfo(subjectInfo map[string]*TestSubject)
SetSubjectInfo is for configuring subject passwords when you wish to have login prompts for interactive testing.
func (*TestProvider) SetSupportedScopes ¶
func (p *TestProvider) SetSupportedScopes(scope ...string)
SetSupportedScopes sets the values for the scopes supported for authorization. Valid supported scopes are: openid, profile, email, address, phone
func (*TestProvider) SetUserInfoReply ¶
func (p *TestProvider) SetUserInfoReply(resp map[string]interface{})
SetUserInfoReply sets the UserInfo endpoint response.
func (*TestProvider) SigningKeys ¶
func (p *TestProvider) SigningKeys() (crypto.PrivateKey, crypto.PublicKey, Alg, string)
SigningKeys returns the test provider's keys used to sign JWTs, its Alg and Key ID.
func (*TestProvider) SubjectInfo ¶
func (p *TestProvider) SubjectInfo() map[string]*TestSubject
SubjectInfo returns the current subject passwords when you wish to have login prompts for interactive testing.
func (*TestProvider) SupportedScopes ¶
func (p *TestProvider) SupportedScopes() []string
SupportedScopes returns the values for the scopes supported.
func (*TestProvider) UserInfoReply ¶
func (p *TestProvider) UserInfoReply() map[string]interface{}
UserInfoReply gets the UserInfo endpoint response.
type TestProviderDefaults ¶
type TestProviderDefaults struct { // ClientID for test relying party which is empty by default ClientID *string // ClientSecret for test relying party which is empty by default ClientSecret *string // ExpectedSubject configures the expected subject for any JWTs issued by // the provider (the default is "alice@example.com") ExpectedSubject *string // ExpectedCode configures the auth code required by the /authorize endpoint // and the code is empty by default ExpectedCode *string // ExpectedState configures the value for the state parameter returned from // the /authorize endpoint which is empty by default ExpectedState *string // ExpectedAuthNonce configures the nonce value required for /authorize // endpoint which is empty by default ExpectedNonce *string // AllowedRedirectURIs configures the allowed redirect URIs for the OIDC // workflow which is "https://example.com" by default AllowedRedirectURIs []string // UserInfoReply configures the UserInfo endpoint response. There is a // basic response for sub == "alice@example.com" by default. UserInfoReply map[string]interface{} // SupportedScopes configures the supported scopes which is "openid" by // default SupportedScopes []string // CustomAudiences configures what audience value to embed in the JWT issued // by the OIDC workflow. By default only the ClientId is in the aud claim // returned. CustomAudiences []string // CustomClaims configures the custom claims added to JWTs returned. By // default there are no additional custom claims CustomClaims map[string]interface{} // SigningKey configures the signing key and algorithm for JWTs returned. // By default an ES256 key is generated and used. SigningKey *TestSigningKey // Expiry configures the expiry for JWTs returned and now + 5 * time.Second // is the default Expiry *time.Duration // NowFunc configures how the test provider will determine the current time // The default is time.Now() NowFunc func() time.Time // PKCEVerifier(oidc.CodeVerifier) configures the PKCE code_verifier PKCEVerifier CodeVerifier // OmitAuthTime turn on/off the omitting of an auth_time claim from // id_tokens from the /token endpoint. If set to true, the test provider will // not include the auth_time claim in issued id_tokens from the /token // endpoint. The default is false, so auth_time will be included OmitAuthTime bool // OmitIDTokens turn on/off the omitting of id_tokens from the /token // endpoint. If set to true, the test provider will not omit (issue) id_tokens // from the /token endpoint. The default is false, so ID tokens will be included OmitIDTokens bool // OmitAccessTokens turn on/off the omitting of access_tokens from the /token // endpoint. If set to true, the test provider will not omit (issue) // access_tokens from the /token endpoint. The default is false, so Access // tokens will be included OmitAccessTokens bool // DisableTokenEndpoint makes the /token endpoint return 401. It is false by // default, so the endpoint is on DisableTokenEndpoint bool // DisableImplicitFlow disables implicit flow responses, causing them to // return a 401 http status. The implicit flow is allowed by default DisableImplicitFlow bool // DisableUserInfo disables userinfo responses, causing it to return a 404 // http status. The userinfo endpoint is enabled by default DisableUserInfo bool // DisableJWKs disables the JWKs endpoint, causing it to 404. It is enabled // by default DisableJWKs bool // InvalidJWKS makes the JWKs endpoint return an invalid response. Valid // JWKs are returned by default. InvalidJWKS bool // SubjectInfo configures a subject/password dictionary. If configured, // then an interactive Login form is presented by the /authorize endpoint // and the TestProvider becomes an interactive test provider using the // provided subject/password dictionary. SubjectInfo map[string]*TestSubject }
TestProviderDefaults define a type for composing all the defaults for StartTestProvider(...)
type TestSigningKey ¶
type TestSigningKey struct { Alg Alg PrivKey crypto.PrivateKey PubKey crypto.PublicKey }
TestSigningKey defines a type for specifying a test signing key and algorithm when providing TestProviderDefaults
type TestSubject ¶
type TestSubject struct { Password string UserInfo map[string]interface{} CustomClaims map[string]interface{} }
TestSubject is a struct that contains various values for customizing per-user responses via SubjectInfo in TestProvider. See the description of those values in TestProvider; these are simply overrides.
type TestingLogger ¶
type TestingLogger struct {
Logger hclog.Logger
}
TestingLogger defines a logger that will implement the TestingT interface so it can be used with StartTestProvider(...) as its t TestingT parameter.
func NewTestingLogger ¶
func NewTestingLogger(logger hclog.Logger) (*TestingLogger, error)
NewTestingLogger makes a new TestingLogger
func (*TestingLogger) Errorf ¶
func (l *TestingLogger) Errorf(format string, args ...interface{})
Errorf will output the error to the log
func (*TestingLogger) Infof ¶
func (l *TestingLogger) Infof(format string, args ...interface{})
Infof will output the info to the log
type TestingT ¶
type TestingT interface { Errorf(format string, args ...interface{}) FailNow() }
TestingT defines a very slim interface required by the TestProvider and any test functions it uses.
type Tk ¶
type Tk struct {
// contains filtered or unexported fields
}
Tk satisfies the Token interface and represents an Oauth2 access_token and refresh_token (including the the access_token expiry), as well as an OIDC id_token. The access_token and refresh_token may be empty.
func NewToken ¶
NewToken creates a new Token (*Tk). The IDToken is required and the *oauth2.Token may be nil. Supports the WithNow option (with a default to time.Now).
func (*Tk) AccessToken ¶
func (t *Tk) AccessToken() AccessToken
AccessToken implements the Token.AccessToken() interface function and may return an empty AccessToken.
func (*Tk) Expiry ¶
Expiry implements the Token.Expiry() interface function and may return a "zero" time if the token's AccessToken is empty.
func (*Tk) RefreshToken ¶
func (t *Tk) RefreshToken() RefreshToken
RefreshToken implements the Token.RefreshToken() interface function and may return an empty RefreshToken.
func (*Tk) StaticTokenSource ¶
func (t *Tk) StaticTokenSource() oauth2.TokenSource
StaticTokenSource returns a TokenSource that always returns the same token. Because the provided token t is never refreshed. It will return nil, if the t.AccessToken() is empty.
type Token ¶
type Token interface { // RefreshToken returns the Token's refresh_token. RefreshToken() RefreshToken // AccessToken returns the Token's access_token. AccessToken() AccessToken // IDToken returns the Token's id_token. IDToken() IDToken // Expiry returns the expiration of the access_token. Expiry() time.Time // Valid will ensure that the access_token is not empty or expired. Valid() bool // IsExpired returns true if the token has expired. Implementations should // support a time skew (perhaps TokenExpirySkew) when checking expiration. IsExpired() bool }
Token interface represents an OIDC id_token, as well as an Oauth2 access_token and refresh_token (including the the access_token expiry).
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
callback is a package that provides callbacks (in the form of http.HandlerFunc) for handling OIDC provider responses to authorization code flow (with optional PKCE) and implicit flow authentication attempts.
|
callback is a package that provides callbacks (in the form of http.HandlerFunc) for handling OIDC provider responses to authorization code flow (with optional PKCE) and implicit flow authentication attempts. |
examples
|
|
internal
|
|
base62
Package base62 provides utilities for working with base62 strings.
|
Package base62 provides utilities for working with base62 strings. |