diff options
Diffstat (limited to 'kube2msb/src/vendor/github.com/coreos/go-oidc/oidc/client.go')
-rw-r--r-- | kube2msb/src/vendor/github.com/coreos/go-oidc/oidc/client.go | 846 |
1 files changed, 0 insertions, 846 deletions
diff --git a/kube2msb/src/vendor/github.com/coreos/go-oidc/oidc/client.go b/kube2msb/src/vendor/github.com/coreos/go-oidc/oidc/client.go deleted file mode 100644 index 7a3cb40..0000000 --- a/kube2msb/src/vendor/github.com/coreos/go-oidc/oidc/client.go +++ /dev/null @@ -1,846 +0,0 @@ -package oidc - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "net/mail" - "net/url" - "sync" - "time" - - phttp "github.com/coreos/go-oidc/http" - "github.com/coreos/go-oidc/jose" - "github.com/coreos/go-oidc/key" - "github.com/coreos/go-oidc/oauth2" -) - -const ( - // amount of time that must pass after the last key sync - // completes before another attempt may begin - keySyncWindow = 5 * time.Second -) - -var ( - DefaultScope = []string{"openid", "email", "profile"} - - supportedAuthMethods = map[string]struct{}{ - oauth2.AuthMethodClientSecretBasic: struct{}{}, - oauth2.AuthMethodClientSecretPost: struct{}{}, - } -) - -type ClientCredentials oauth2.ClientCredentials - -type ClientIdentity struct { - Credentials ClientCredentials - Metadata ClientMetadata -} - -type JWAOptions struct { - // SigningAlg specifies an JWA alg for signing JWTs. - // - // Specifying this field implies different actions depending on the context. It may - // require objects be serialized and signed as a JWT instead of plain JSON, or - // require an existing JWT object use the specified alg. - // - // See: http://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata - SigningAlg string - // EncryptionAlg, if provided, specifies that the returned or sent object be stored - // (or nested) within a JWT object and encrypted with the provided JWA alg. - EncryptionAlg string - // EncryptionEnc specifies the JWA enc algorithm to use with EncryptionAlg. If - // EncryptionAlg is provided and EncryptionEnc is omitted, this field defaults - // to A128CBC-HS256. - // - // If EncryptionEnc is provided EncryptionAlg must also be specified. - EncryptionEnc string -} - -func (opt JWAOptions) valid() error { - if opt.EncryptionEnc != "" && opt.EncryptionAlg == "" { - return errors.New("encryption encoding provided with no encryption algorithm") - } - return nil -} - -func (opt JWAOptions) defaults() JWAOptions { - if opt.EncryptionAlg != "" && opt.EncryptionEnc == "" { - opt.EncryptionEnc = jose.EncA128CBCHS256 - } - return opt -} - -var ( - // Ensure ClientMetadata satisfies these interfaces. - _ json.Marshaler = &ClientMetadata{} - _ json.Unmarshaler = &ClientMetadata{} -) - -// ClientMetadata holds metadata that the authorization server associates -// with a client identifier. The fields range from human-facing display -// strings such as client name, to items that impact the security of the -// protocol, such as the list of valid redirect URIs. -// -// See http://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata -// -// TODO: support language specific claim representations -// http://openid.net/specs/openid-connect-registration-1_0.html#LanguagesAndScripts -type ClientMetadata struct { - RedirectURIs []url.URL // Required - - // A list of OAuth 2.0 "response_type" values that the client wishes to restrict - // itself to. Either "code", "token", or another registered extension. - // - // If omitted, only "code" will be used. - ResponseTypes []string - // A list of OAuth 2.0 grant types the client wishes to restrict itself to. - // The grant type values used by OIDC are "authorization_code", "implicit", - // and "refresh_token". - // - // If ommitted, only "authorization_code" will be used. - GrantTypes []string - // "native" or "web". If omitted, "web". - ApplicationType string - - // List of email addresses. - Contacts []mail.Address - // Name of client to be presented to the end-user. - ClientName string - // URL that references a logo for the Client application. - LogoURI *url.URL - // URL of the home page of the Client. - ClientURI *url.URL - // Profile data policies and terms of use to be provided to the end user. - PolicyURI *url.URL - TermsOfServiceURI *url.URL - - // URL to or the value of the client's JSON Web Key Set document. - JWKSURI *url.URL - JWKS *jose.JWKSet - - // URL referencing a flie with a single JSON array of redirect URIs. - SectorIdentifierURI *url.URL - - SubjectType string - - // Options to restrict the JWS alg and enc values used for server responses and requests. - IDTokenResponseOptions JWAOptions - UserInfoResponseOptions JWAOptions - RequestObjectOptions JWAOptions - - // Client requested authorization method and signing options for the token endpoint. - // - // Defaults to "client_secret_basic" - TokenEndpointAuthMethod string - TokenEndpointAuthSigningAlg string - - // DefaultMaxAge specifies the maximum amount of time in seconds before an authorized - // user must reauthroize. - // - // If 0, no limitation is placed on the maximum. - DefaultMaxAge int64 - // RequireAuthTime specifies if the auth_time claim in the ID token is required. - RequireAuthTime bool - - // Default Authentication Context Class Reference values for authentication requests. - DefaultACRValues []string - - // URI that a third party can use to initiate a login by the relaying party. - // - // See: http://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin - InitiateLoginURI *url.URL - // Pre-registered request_uri values that may be cached by the server. - RequestURIs []url.URL -} - -// Defaults returns a shallow copy of ClientMetadata with default -// values replacing omitted fields. -func (m ClientMetadata) Defaults() ClientMetadata { - if len(m.ResponseTypes) == 0 { - m.ResponseTypes = []string{oauth2.ResponseTypeCode} - } - if len(m.GrantTypes) == 0 { - m.GrantTypes = []string{oauth2.GrantTypeAuthCode} - } - if m.ApplicationType == "" { - m.ApplicationType = "web" - } - if m.TokenEndpointAuthMethod == "" { - m.TokenEndpointAuthMethod = oauth2.AuthMethodClientSecretBasic - } - m.IDTokenResponseOptions = m.IDTokenResponseOptions.defaults() - m.UserInfoResponseOptions = m.UserInfoResponseOptions.defaults() - m.RequestObjectOptions = m.RequestObjectOptions.defaults() - return m -} - -func (m *ClientMetadata) MarshalJSON() ([]byte, error) { - e := m.toEncodableStruct() - return json.Marshal(&e) -} - -func (m *ClientMetadata) UnmarshalJSON(data []byte) error { - var e encodableClientMetadata - if err := json.Unmarshal(data, &e); err != nil { - return err - } - meta, err := e.toStruct() - if err != nil { - return err - } - if err := meta.Valid(); err != nil { - return err - } - *m = meta - return nil -} - -type encodableClientMetadata struct { - RedirectURIs []string `json:"redirect_uris"` // Required - ResponseTypes []string `json:"response_types,omitempty"` - GrantTypes []string `json:"grant_types,omitempty"` - ApplicationType string `json:"application_type,omitempty"` - Contacts []string `json:"contacts,omitempty"` - ClientName string `json:"client_name,omitempty"` - LogoURI string `json:"logo_uri,omitempty"` - ClientURI string `json:"client_uri,omitempty"` - PolicyURI string `json:"policy_uri,omitempty"` - TermsOfServiceURI string `json:"tos_uri,omitempty"` - JWKSURI string `json:"jwks_uri,omitempty"` - JWKS *jose.JWKSet `json:"jwks,omitempty"` - SectorIdentifierURI string `json:"sector_identifier_uri,omitempty"` - SubjectType string `json:"subject_type,omitempty"` - IDTokenSignedResponseAlg string `json:"id_token_signed_response_alg,omitempty"` - IDTokenEncryptedResponseAlg string `json:"id_token_encrypted_response_alg,omitempty"` - IDTokenEncryptedResponseEnc string `json:"id_token_encrypted_response_enc,omitempty"` - UserInfoSignedResponseAlg string `json:"userinfo_signed_response_alg,omitempty"` - UserInfoEncryptedResponseAlg string `json:"userinfo_encrypted_response_alg,omitempty"` - UserInfoEncryptedResponseEnc string `json:"userinfo_encrypted_response_enc,omitempty"` - RequestObjectSigningAlg string `json:"request_object_signing_alg,omitempty"` - RequestObjectEncryptionAlg string `json:"request_object_encryption_alg,omitempty"` - RequestObjectEncryptionEnc string `json:"request_object_encryption_enc,omitempty"` - TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"` - TokenEndpointAuthSigningAlg string `json:"token_endpoint_auth_signing_alg,omitempty"` - DefaultMaxAge int64 `json:"default_max_age,omitempty"` - RequireAuthTime bool `json:"require_auth_time,omitempty"` - DefaultACRValues []string `json:"default_acr_values,omitempty"` - InitiateLoginURI string `json:"initiate_login_uri,omitempty"` - RequestURIs []string `json:"request_uris,omitempty"` -} - -func (c *encodableClientMetadata) toStruct() (ClientMetadata, error) { - p := stickyErrParser{} - m := ClientMetadata{ - RedirectURIs: p.parseURIs(c.RedirectURIs, "redirect_uris"), - ResponseTypes: c.ResponseTypes, - GrantTypes: c.GrantTypes, - ApplicationType: c.ApplicationType, - Contacts: p.parseEmails(c.Contacts, "contacts"), - ClientName: c.ClientName, - LogoURI: p.parseURI(c.LogoURI, "logo_uri"), - ClientURI: p.parseURI(c.ClientURI, "client_uri"), - PolicyURI: p.parseURI(c.PolicyURI, "policy_uri"), - TermsOfServiceURI: p.parseURI(c.TermsOfServiceURI, "tos_uri"), - JWKSURI: p.parseURI(c.JWKSURI, "jwks_uri"), - JWKS: c.JWKS, - SectorIdentifierURI: p.parseURI(c.SectorIdentifierURI, "sector_identifier_uri"), - SubjectType: c.SubjectType, - TokenEndpointAuthMethod: c.TokenEndpointAuthMethod, - TokenEndpointAuthSigningAlg: c.TokenEndpointAuthSigningAlg, - DefaultMaxAge: c.DefaultMaxAge, - RequireAuthTime: c.RequireAuthTime, - DefaultACRValues: c.DefaultACRValues, - InitiateLoginURI: p.parseURI(c.InitiateLoginURI, "initiate_login_uri"), - RequestURIs: p.parseURIs(c.RequestURIs, "request_uris"), - IDTokenResponseOptions: JWAOptions{ - c.IDTokenSignedResponseAlg, - c.IDTokenEncryptedResponseAlg, - c.IDTokenEncryptedResponseEnc, - }, - UserInfoResponseOptions: JWAOptions{ - c.UserInfoSignedResponseAlg, - c.UserInfoEncryptedResponseAlg, - c.UserInfoEncryptedResponseEnc, - }, - RequestObjectOptions: JWAOptions{ - c.RequestObjectSigningAlg, - c.RequestObjectEncryptionAlg, - c.RequestObjectEncryptionEnc, - }, - } - if p.firstErr != nil { - return ClientMetadata{}, p.firstErr - } - return m, nil -} - -// stickyErrParser parses URIs and email addresses. Once it encounters -// a parse error, subsequent calls become no-op. -type stickyErrParser struct { - firstErr error -} - -func (p *stickyErrParser) parseURI(s, field string) *url.URL { - if p.firstErr != nil || s == "" { - return nil - } - u, err := url.Parse(s) - if err == nil { - if u.Host == "" { - err = errors.New("no host in URI") - } else if u.Scheme != "http" && u.Scheme != "https" { - err = errors.New("invalid URI scheme") - } - } - if err != nil { - p.firstErr = fmt.Errorf("failed to parse %s: %v", field, err) - return nil - } - return u -} - -func (p *stickyErrParser) parseURIs(s []string, field string) []url.URL { - if p.firstErr != nil || len(s) == 0 { - return nil - } - uris := make([]url.URL, len(s)) - for i, val := range s { - if val == "" { - p.firstErr = fmt.Errorf("invalid URI in field %s", field) - return nil - } - if u := p.parseURI(val, field); u != nil { - uris[i] = *u - } - } - return uris -} - -func (p *stickyErrParser) parseEmails(s []string, field string) []mail.Address { - if p.firstErr != nil || len(s) == 0 { - return nil - } - addrs := make([]mail.Address, len(s)) - for i, addr := range s { - if addr == "" { - p.firstErr = fmt.Errorf("invalid email in field %s", field) - return nil - } - a, err := mail.ParseAddress(addr) - if err != nil { - p.firstErr = fmt.Errorf("invalid email in field %s: %v", field, err) - return nil - } - addrs[i] = *a - } - return addrs -} - -func (m *ClientMetadata) toEncodableStruct() encodableClientMetadata { - return encodableClientMetadata{ - RedirectURIs: urisToStrings(m.RedirectURIs), - ResponseTypes: m.ResponseTypes, - GrantTypes: m.GrantTypes, - ApplicationType: m.ApplicationType, - Contacts: emailsToStrings(m.Contacts), - ClientName: m.ClientName, - LogoURI: uriToString(m.LogoURI), - ClientURI: uriToString(m.ClientURI), - PolicyURI: uriToString(m.PolicyURI), - TermsOfServiceURI: uriToString(m.TermsOfServiceURI), - JWKSURI: uriToString(m.JWKSURI), - JWKS: m.JWKS, - SectorIdentifierURI: uriToString(m.SectorIdentifierURI), - SubjectType: m.SubjectType, - IDTokenSignedResponseAlg: m.IDTokenResponseOptions.SigningAlg, - IDTokenEncryptedResponseAlg: m.IDTokenResponseOptions.EncryptionAlg, - IDTokenEncryptedResponseEnc: m.IDTokenResponseOptions.EncryptionEnc, - UserInfoSignedResponseAlg: m.UserInfoResponseOptions.SigningAlg, - UserInfoEncryptedResponseAlg: m.UserInfoResponseOptions.EncryptionAlg, - UserInfoEncryptedResponseEnc: m.UserInfoResponseOptions.EncryptionEnc, - RequestObjectSigningAlg: m.RequestObjectOptions.SigningAlg, - RequestObjectEncryptionAlg: m.RequestObjectOptions.EncryptionAlg, - RequestObjectEncryptionEnc: m.RequestObjectOptions.EncryptionEnc, - TokenEndpointAuthMethod: m.TokenEndpointAuthMethod, - TokenEndpointAuthSigningAlg: m.TokenEndpointAuthSigningAlg, - DefaultMaxAge: m.DefaultMaxAge, - RequireAuthTime: m.RequireAuthTime, - DefaultACRValues: m.DefaultACRValues, - InitiateLoginURI: uriToString(m.InitiateLoginURI), - RequestURIs: urisToStrings(m.RequestURIs), - } -} - -func uriToString(u *url.URL) string { - if u == nil { - return "" - } - return u.String() -} - -func urisToStrings(urls []url.URL) []string { - if len(urls) == 0 { - return nil - } - sli := make([]string, len(urls)) - for i, u := range urls { - sli[i] = u.String() - } - return sli -} - -func emailsToStrings(addrs []mail.Address) []string { - if len(addrs) == 0 { - return nil - } - sli := make([]string, len(addrs)) - for i, addr := range addrs { - sli[i] = addr.String() - } - return sli -} - -// Valid determines if a ClientMetadata conforms with the OIDC specification. -// -// Valid is called by UnmarshalJSON. -// -// NOTE(ericchiang): For development purposes Valid does not mandate 'https' for -// URLs fields where the OIDC spec requires it. This may change in future releases -// of this package. See: https://github.com/coreos/go-oidc/issues/34 -func (m *ClientMetadata) Valid() error { - if len(m.RedirectURIs) == 0 { - return errors.New("zero redirect URLs") - } - - validURI := func(u *url.URL, fieldName string) error { - if u.Host == "" { - return fmt.Errorf("no host for uri field %s", fieldName) - } - if u.Scheme != "http" && u.Scheme != "https" { - return fmt.Errorf("uri field %s scheme is not http or https", fieldName) - } - return nil - } - - uris := []struct { - val *url.URL - name string - }{ - {m.LogoURI, "logo_uri"}, - {m.ClientURI, "client_uri"}, - {m.PolicyURI, "policy_uri"}, - {m.TermsOfServiceURI, "tos_uri"}, - {m.JWKSURI, "jwks_uri"}, - {m.SectorIdentifierURI, "sector_identifier_uri"}, - {m.InitiateLoginURI, "initiate_login_uri"}, - } - - for _, uri := range uris { - if uri.val == nil { - continue - } - if err := validURI(uri.val, uri.name); err != nil { - return err - } - } - - uriLists := []struct { - vals []url.URL - name string - }{ - {m.RedirectURIs, "redirect_uris"}, - {m.RequestURIs, "request_uris"}, - } - for _, list := range uriLists { - for _, uri := range list.vals { - if err := validURI(&uri, list.name); err != nil { - return err - } - } - } - - options := []struct { - option JWAOptions - name string - }{ - {m.IDTokenResponseOptions, "id_token response"}, - {m.UserInfoResponseOptions, "userinfo response"}, - {m.RequestObjectOptions, "request_object"}, - } - for _, option := range options { - if err := option.option.valid(); err != nil { - return fmt.Errorf("invalid JWA values for %s: %v", option.name, err) - } - } - return nil -} - -type ClientRegistrationResponse struct { - ClientID string // Required - ClientSecret string - RegistrationAccessToken string - RegistrationClientURI string - // If IsZero is true, unspecified. - ClientIDIssuedAt time.Time - // Time at which the client_secret will expire. - // If IsZero is true, it will not expire. - ClientSecretExpiresAt time.Time - - ClientMetadata -} - -type encodableClientRegistrationResponse struct { - ClientID string `json:"client_id"` // Required - ClientSecret string `json:"client_secret,omitempty"` - RegistrationAccessToken string `json:"registration_access_token,omitempty"` - RegistrationClientURI string `json:"registration_client_uri,omitempty"` - ClientIDIssuedAt int64 `json:"client_id_issued_at,omitempty"` - // Time at which the client_secret will expire, in seconds since the epoch. - // If 0 it will not expire. - ClientSecretExpiresAt int64 `json:"client_secret_expires_at"` // Required - - encodableClientMetadata -} - -func unixToSec(t time.Time) int64 { - if t.IsZero() { - return 0 - } - return t.Unix() -} - -func (c *ClientRegistrationResponse) MarshalJSON() ([]byte, error) { - e := encodableClientRegistrationResponse{ - ClientID: c.ClientID, - ClientSecret: c.ClientSecret, - RegistrationAccessToken: c.RegistrationAccessToken, - RegistrationClientURI: c.RegistrationClientURI, - ClientIDIssuedAt: unixToSec(c.ClientIDIssuedAt), - ClientSecretExpiresAt: unixToSec(c.ClientSecretExpiresAt), - encodableClientMetadata: c.ClientMetadata.toEncodableStruct(), - } - return json.Marshal(&e) -} - -func secToUnix(sec int64) time.Time { - if sec == 0 { - return time.Time{} - } - return time.Unix(sec, 0) -} - -func (c *ClientRegistrationResponse) UnmarshalJSON(data []byte) error { - var e encodableClientRegistrationResponse - if err := json.Unmarshal(data, &e); err != nil { - return err - } - if e.ClientID == "" { - return errors.New("no client_id in client registration response") - } - metadata, err := e.encodableClientMetadata.toStruct() - if err != nil { - return err - } - *c = ClientRegistrationResponse{ - ClientID: e.ClientID, - ClientSecret: e.ClientSecret, - RegistrationAccessToken: e.RegistrationAccessToken, - RegistrationClientURI: e.RegistrationClientURI, - ClientIDIssuedAt: secToUnix(e.ClientIDIssuedAt), - ClientSecretExpiresAt: secToUnix(e.ClientSecretExpiresAt), - ClientMetadata: metadata, - } - return nil -} - -type ClientConfig struct { - HTTPClient phttp.Client - Credentials ClientCredentials - Scope []string - RedirectURL string - ProviderConfig ProviderConfig - KeySet key.PublicKeySet -} - -func NewClient(cfg ClientConfig) (*Client, error) { - // Allow empty redirect URL in the case where the client - // only needs to verify a given token. - ru, err := url.Parse(cfg.RedirectURL) - if err != nil { - return nil, fmt.Errorf("invalid redirect URL: %v", err) - } - - c := Client{ - credentials: cfg.Credentials, - httpClient: cfg.HTTPClient, - scope: cfg.Scope, - redirectURL: ru.String(), - providerConfig: newProviderConfigRepo(cfg.ProviderConfig), - keySet: cfg.KeySet, - } - - if c.httpClient == nil { - c.httpClient = http.DefaultClient - } - - if c.scope == nil { - c.scope = make([]string, len(DefaultScope)) - copy(c.scope, DefaultScope) - } - - return &c, nil -} - -type Client struct { - httpClient phttp.Client - providerConfig *providerConfigRepo - credentials ClientCredentials - redirectURL string - scope []string - keySet key.PublicKeySet - providerSyncer *ProviderConfigSyncer - - keySetSyncMutex sync.RWMutex - lastKeySetSync time.Time -} - -func (c *Client) Healthy() error { - now := time.Now().UTC() - - cfg := c.providerConfig.Get() - - if cfg.Empty() { - return errors.New("oidc client provider config empty") - } - - if !cfg.ExpiresAt.IsZero() && cfg.ExpiresAt.Before(now) { - return errors.New("oidc client provider config expired") - } - - return nil -} - -func (c *Client) OAuthClient() (*oauth2.Client, error) { - cfg := c.providerConfig.Get() - authMethod, err := chooseAuthMethod(cfg) - if err != nil { - return nil, err - } - - ocfg := oauth2.Config{ - Credentials: oauth2.ClientCredentials(c.credentials), - RedirectURL: c.redirectURL, - AuthURL: cfg.AuthEndpoint.String(), - TokenURL: cfg.TokenEndpoint.String(), - Scope: c.scope, - AuthMethod: authMethod, - } - - return oauth2.NewClient(c.httpClient, ocfg) -} - -func chooseAuthMethod(cfg ProviderConfig) (string, error) { - if len(cfg.TokenEndpointAuthMethodsSupported) == 0 { - return oauth2.AuthMethodClientSecretBasic, nil - } - - for _, authMethod := range cfg.TokenEndpointAuthMethodsSupported { - if _, ok := supportedAuthMethods[authMethod]; ok { - return authMethod, nil - } - } - - return "", errors.New("no supported auth methods") -} - -// SyncProviderConfig starts the provider config syncer -func (c *Client) SyncProviderConfig(discoveryURL string) chan struct{} { - r := NewHTTPProviderConfigGetter(c.httpClient, discoveryURL) - s := NewProviderConfigSyncer(r, c.providerConfig) - stop := s.Run() - s.WaitUntilInitialSync() - return stop -} - -func (c *Client) maybeSyncKeys() error { - tooSoon := func() bool { - return time.Now().UTC().Before(c.lastKeySetSync.Add(keySyncWindow)) - } - - // ignore request to sync keys if a sync operation has been - // attempted too recently - if tooSoon() { - return nil - } - - c.keySetSyncMutex.Lock() - defer c.keySetSyncMutex.Unlock() - - // check again, as another goroutine may have been holding - // the lock while updating the keys - if tooSoon() { - return nil - } - - cfg := c.providerConfig.Get() - r := NewRemotePublicKeyRepo(c.httpClient, cfg.KeysEndpoint.String()) - w := &clientKeyRepo{client: c} - _, err := key.Sync(r, w) - c.lastKeySetSync = time.Now().UTC() - - return err -} - -type clientKeyRepo struct { - client *Client -} - -func (r *clientKeyRepo) Set(ks key.KeySet) error { - pks, ok := ks.(*key.PublicKeySet) - if !ok { - return errors.New("unable to cast to PublicKey") - } - r.client.keySet = *pks - return nil -} - -func (c *Client) ClientCredsToken(scope []string) (jose.JWT, error) { - cfg := c.providerConfig.Get() - - if !cfg.SupportsGrantType(oauth2.GrantTypeClientCreds) { - return jose.JWT{}, fmt.Errorf("%v grant type is not supported", oauth2.GrantTypeClientCreds) - } - - oac, err := c.OAuthClient() - if err != nil { - return jose.JWT{}, err - } - - t, err := oac.ClientCredsToken(scope) - if err != nil { - return jose.JWT{}, err - } - - jwt, err := jose.ParseJWT(t.IDToken) - if err != nil { - return jose.JWT{}, err - } - - return jwt, c.VerifyJWT(jwt) -} - -// ExchangeAuthCode exchanges an OAuth2 auth code for an OIDC JWT ID token. -func (c *Client) ExchangeAuthCode(code string) (jose.JWT, error) { - oac, err := c.OAuthClient() - if err != nil { - return jose.JWT{}, err - } - - t, err := oac.RequestToken(oauth2.GrantTypeAuthCode, code) - if err != nil { - return jose.JWT{}, err - } - - jwt, err := jose.ParseJWT(t.IDToken) - if err != nil { - return jose.JWT{}, err - } - - return jwt, c.VerifyJWT(jwt) -} - -// RefreshToken uses a refresh token to exchange for a new OIDC JWT ID Token. -func (c *Client) RefreshToken(refreshToken string) (jose.JWT, error) { - oac, err := c.OAuthClient() - if err != nil { - return jose.JWT{}, err - } - - t, err := oac.RequestToken(oauth2.GrantTypeRefreshToken, refreshToken) - if err != nil { - return jose.JWT{}, err - } - - jwt, err := jose.ParseJWT(t.IDToken) - if err != nil { - return jose.JWT{}, err - } - - return jwt, c.VerifyJWT(jwt) -} - -func (c *Client) VerifyJWT(jwt jose.JWT) error { - var keysFunc func() []key.PublicKey - if kID, ok := jwt.KeyID(); ok { - keysFunc = c.keysFuncWithID(kID) - } else { - keysFunc = c.keysFuncAll() - } - - v := NewJWTVerifier( - c.providerConfig.Get().Issuer.String(), - c.credentials.ID, - c.maybeSyncKeys, keysFunc) - - return v.Verify(jwt) -} - -// keysFuncWithID returns a function that retrieves at most unexpired -// public key from the Client that matches the provided ID -func (c *Client) keysFuncWithID(kID string) func() []key.PublicKey { - return func() []key.PublicKey { - c.keySetSyncMutex.RLock() - defer c.keySetSyncMutex.RUnlock() - - if c.keySet.ExpiresAt().Before(time.Now()) { - return []key.PublicKey{} - } - - k := c.keySet.Key(kID) - if k == nil { - return []key.PublicKey{} - } - - return []key.PublicKey{*k} - } -} - -// keysFuncAll returns a function that retrieves all unexpired public -// keys from the Client -func (c *Client) keysFuncAll() func() []key.PublicKey { - return func() []key.PublicKey { - c.keySetSyncMutex.RLock() - defer c.keySetSyncMutex.RUnlock() - - if c.keySet.ExpiresAt().Before(time.Now()) { - return []key.PublicKey{} - } - - return c.keySet.Keys() - } -} - -type providerConfigRepo struct { - mu sync.RWMutex - config ProviderConfig // do not access directly, use Get() -} - -func newProviderConfigRepo(pc ProviderConfig) *providerConfigRepo { - return &providerConfigRepo{sync.RWMutex{}, pc} -} - -// returns an error to implement ProviderConfigSetter -func (r *providerConfigRepo) Set(cfg ProviderConfig) error { - r.mu.Lock() - defer r.mu.Unlock() - r.config = cfg - return nil -} - -func (r *providerConfigRepo) Get() ProviderConfig { - r.mu.RLock() - defer r.mu.RUnlock() - return r.config -} |