feat: Token Revocation, Request Object and OP Certification (#130)

FEATURES (and FIXES):
- support OAuth 2.0 Token Revocation [RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)
- handle request object using `request` parameter [OIDC Core 1.0 Request Object](https://openid.net/specs/openid-connect-core-1_0.html#RequestObject)
- handle response mode
- added some information to the discovery endpoint:
  - revocation_endpoint (added with token revocation) 
  - revocation_endpoint_auth_methods_supported (added with token revocation)
  - revocation_endpoint_auth_signing_alg_values_supported (added with token revocation)
  - token_endpoint_auth_signing_alg_values_supported (was missing)
  - introspection_endpoint_auth_signing_alg_values_supported (was missing)
  - request_object_signing_alg_values_supported (added with request object)
  - request_parameter_supported (added with request object)
 - fixed `removeUserinfoScopes ` now returns the scopes without "userinfo" scopes (profile, email, phone, addedd) [source diff](https://github.com/caos/oidc/pull/130/files#diff-fad50c8c0f065d4dbc49d6c6a38f09c992c8f5d651a479ba00e31b500543559eL170-R171)
- improved error handling (pkg/oidc/error.go) and fixed some wrong OAuth errors (e.g. `invalid_grant` instead of `invalid_request`)
- improved MarshalJSON and added MarshalJSONWithStatus
- removed deprecated PEM decryption from `BytesToPrivateKey`  [source diff](https://github.com/caos/oidc/pull/130/files#diff-fe246e428e399ccff599627c71764de51387b60b4df84c67de3febd0954e859bL11-L19)
- NewAccessTokenVerifier now uses correct (internal) `accessTokenVerifier` [source diff](https://github.com/caos/oidc/pull/130/files#diff-3a01c7500ead8f35448456ef231c7c22f8d291710936cac91de5edeef52ffc72L52-R52)

BREAKING CHANGE:
- move functions from `utils` package into separate packages
- added various methods to the (OP) `Configuration` interface [source diff](https://github.com/caos/oidc/pull/130/files#diff-2538e0dfc772fdc37f057aecd6fcc2943f516c24e8be794cce0e368a26d20a82R19-R32)
- added revocationEndpoint to `WithCustomEndpoints ` [source diff](https://github.com/caos/oidc/pull/130/files#diff-19ae13a743eb7cebbb96492798b1bec556673eb6236b1387e38d722900bae1c3L355-R391)
- remove unnecessary context parameter from JWTProfileExchange [source diff](https://github.com/caos/oidc/pull/130/files#diff-4ed8f6affa4a9631fa8a034b3d5752fbb6a819107141aae00029014e950f7b4cL14)
This commit is contained in:
Livio Amstutz 2021-11-02 13:21:35 +01:00 committed by GitHub
parent 763d3334e7
commit eb10752e48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 1738 additions and 624 deletions

View file

@ -42,6 +42,9 @@ const (
DisplayTouch Display = "touch"
DisplayWAP Display = "wap"
ResponseModeQuery ResponseMode = "query"
ResponseModeFragment ResponseMode = "fragment"
//PromptNone (`none`) disallows the Authorization Server to display any authentication or consent user interface pages.
//An error (login_required, interaction_required, ...) will be returned if the user is not already authenticated or consent is needed
PromptNone = "none"
@ -59,27 +62,28 @@ const (
//AuthRequest according to:
//https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
type AuthRequest struct {
ID string
Scopes SpaceDelimitedArray `schema:"scope"`
ResponseType ResponseType `schema:"response_type"`
ClientID string `schema:"client_id"`
RedirectURI string `schema:"redirect_uri"` //TODO: type
Scopes SpaceDelimitedArray `json:"scope" schema:"scope"`
ResponseType ResponseType `json:"response_type" schema:"response_type"`
ClientID string `json:"client_id" schema:"client_id"`
RedirectURI string `json:"redirect_uri" schema:"redirect_uri"`
State string `schema:"state"`
State string `json:"state" schema:"state"`
Nonce string `json:"nonce" schema:"nonce"`
// ResponseMode TODO: ?
ResponseMode ResponseMode `json:"response_mode" schema:"response_mode"`
Display Display `json:"display" schema:"display"`
Prompt SpaceDelimitedArray `json:"prompt" schema:"prompt"`
MaxAge *uint `json:"max_age" schema:"max_age"`
UILocales Locales `json:"ui_locales" schema:"ui_locales"`
IDTokenHint string `json:"id_token_hint" schema:"id_token_hint"`
LoginHint string `json:"login_hint" schema:"login_hint"`
ACRValues []string `json:"acr_values" schema:"acr_values"`
Nonce string `schema:"nonce"`
Display Display `schema:"display"`
Prompt SpaceDelimitedArray `schema:"prompt"`
MaxAge *uint `schema:"max_age"`
UILocales Locales `schema:"ui_locales"`
IDTokenHint string `schema:"id_token_hint"`
LoginHint string `schema:"login_hint"`
ACRValues []string `schema:"acr_values"`
CodeChallenge string `json:"code_challenge" schema:"code_challenge"`
CodeChallengeMethod CodeChallengeMethod `json:"code_challenge_method" schema:"code_challenge_method"`
CodeChallenge string `schema:"code_challenge"`
CodeChallengeMethod CodeChallengeMethod `schema:"code_challenge_method"`
//RequestParam enables OIDC requests to be passed in a single, self-contained parameter (as JWT, called Request Object)
RequestParam string `schema:"request"`
}
//GetRedirectURI returns the redirect_uri value for the ErrAuthRequest interface

View file

@ -3,7 +3,7 @@ package oidc
import (
"crypto/sha256"
"github.com/caos/oidc/pkg/utils"
"github.com/caos/oidc/pkg/crypto"
)
const (
@ -19,7 +19,7 @@ type CodeChallenge struct {
}
func NewSHACodeChallenge(code string) string {
return utils.HashString(sha256.New(), code, false)
return crypto.HashString(sha256.New(), code, false)
}
func VerifyCodeChallenge(c *CodeChallenge, codeVerifier string) bool {

View file

@ -9,49 +9,144 @@ const (
)
type DiscoveryConfiguration struct {
Issuer string `json:"issuer,omitempty"`
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
TokenEndpoint string `json:"token_endpoint,omitempty"`
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
CheckSessionIframe string `json:"check_session_iframe,omitempty"`
JwksURI string `json:"jwks_uri,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
GrantTypesSupported []GrantType `json:"grant_types_supported,omitempty"`
ACRValuesSupported []string `json:"acr_values_supported,omitempty"`
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
IDTokenEncryptionAlgValuesSupported []string `json:"id_token_encryption_alg_values_supported,omitempty"`
IDTokenEncryptionEncValuesSupported []string `json:"id_token_encryption_enc_values_supported,omitempty"`
UserinfoSigningAlgValuesSupported []string `json:"userinfo_signing_alg_values_supported,omitempty"`
UserinfoEncryptionAlgValuesSupported []string `json:"userinfo_encryption_alg_values_supported,omitempty"`
UserinfoEncryptionEncValuesSupported []string `json:"userinfo_encryption_enc_values_supported,omitempty"`
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported,omitempty"`
RequestObjectEncryptionAlgValuesSupported []string `json:"request_object_encryption_alg_values_supported,omitempty"`
RequestObjectEncryptionEncValuesSupported []string `json:"request_object_encryption_enc_values_supported,omitempty"`
TokenEndpointAuthMethodsSupported []AuthMethod `json:"token_endpoint_auth_methods_supported,omitempty"`
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`
RevocationEndpointAuthMethodsSupported []AuthMethod `json:"revocation_endpoint_auth_methods_supported,omitempty"`
RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"`
IntrospectionEndpointAuthMethodsSupported []AuthMethod `json:"introspection_endpoint_auth_methods_supported,omitempty"`
IntrospectionEndpointAuthSigningAlgValuesSupported []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"`
DisplayValuesSupported []Display `json:"display_values_supported,omitempty"`
ClaimTypesSupported []string `json:"claim_types_supported,omitempty"`
ClaimsSupported []string `json:"claims_supported,omitempty"`
ClaimsParameterSupported bool `json:"claims_parameter_supported,omitempty"`
CodeChallengeMethodsSupported []CodeChallengeMethod `json:"code_challenge_methods_supported,omitempty"`
ServiceDocumentation string `json:"service_documentation,omitempty"`
ClaimsLocalesSupported []language.Tag `json:"claims_locales_supported,omitempty"`
UILocalesSupported []language.Tag `json:"ui_locales_supported,omitempty"`
RequestParameterSupported bool `json:"request_parameter_supported,omitempty"`
RequestURIParameterSupported bool `json:"request_uri_parameter_supported"` //no omitempty because: If omitted, the default value is true
RequireRequestURIRegistration bool `json:"require_request_uri_registration,omitempty"`
OPPolicyURI string `json:"op_policy_uri,omitempty"`
OPTermsOfServiceURI string `json:"op_tos_uri,omitempty"`
//Issuer is the identifier of the OP and is used in the tokens as `iss` claim.
Issuer string `json:"issuer,omitempty"`
//AuthorizationEndpoint is the URL of the OAuth 2.0 Authorization Endpoint where all user interactive login start
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
//TokenEndpoint is the URL of the OAuth 2.0 Token Endpoint where all tokens are issued, except when using Implicit Flow
TokenEndpoint string `json:"token_endpoint,omitempty"`
//IntrospectionEndpoint is the URL of the OAuth 2.0 Introspection Endpoint.
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
//UserinfoEndpoint is the URL where an access_token can be used to retrieve the Userinfo.
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
//RevocationEndpoint is the URL of the OAuth 2.0 Revocation Endpoint.
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
//EndSessionEndpoint is a URL where the RP can perform a redirect to request that the End-User be logged out at the OP.
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
//CheckSessionIframe is a URL where the OP provides an iframe that support cross-origin communications for session state information with the RP Client.
CheckSessionIframe string `json:"check_session_iframe,omitempty"`
//JwksURI is the URL of the JSON Web Key Set. This site contains the signing keys that RPs can use to validate the signature.
//It may also contain the OP's encryption keys that RPs can use to encrypt request to the OP.
JwksURI string `json:"jwks_uri,omitempty"`
//RegistrationEndpoint is the URL for the Dynamic Client Registration.
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
//ScopesSupported lists an array of supported scopes. This list must not include every supported scope by the OP.
ScopesSupported []string `json:"scopes_supported,omitempty"`
//ResponseTypesSupported contains a list of the OAuth 2.0 response_type values that the OP supports (code, id_token, token id_token, ...).
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
//ResponseModesSupported contains a list of the OAuth 2.0 response_mode values that the OP supports. If omitted, the default value is ["query", "fragment"].
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
//GrantTypesSupported contains a list of the OAuth 2.0 grant_type values that the OP supports. If omitted, the default value is ["authorization_code", "implicit"].
GrantTypesSupported []GrantType `json:"grant_types_supported,omitempty"`
//ACRValuesSupported contains a list of Authentication Context Class References that the OP supports.
ACRValuesSupported []string `json:"acr_values_supported,omitempty"`
//SubjectTypesSupported contains a list of Subject Identifier types that the OP supports (pairwise, public).
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
//IDTokenSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for the ID Token.
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
//IDTokenEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for the ID Token.
IDTokenEncryptionAlgValuesSupported []string `json:"id_token_encryption_alg_values_supported,omitempty"`
//IDTokenEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for the ID Token.
IDTokenEncryptionEncValuesSupported []string `json:"id_token_encryption_enc_values_supported,omitempty"`
//UserinfoSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for UserInfo Endpoint.
UserinfoSigningAlgValuesSupported []string `json:"userinfo_signing_alg_values_supported,omitempty"`
//UserinfoEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for the UserInfo Endpoint.
UserinfoEncryptionAlgValuesSupported []string `json:"userinfo_encryption_alg_values_supported,omitempty"`
//UserinfoEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for the UserInfo Endpoint.
UserinfoEncryptionEncValuesSupported []string `json:"userinfo_encryption_enc_values_supported,omitempty"`
//RequestObjectSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the OP for Request Objects.
//These algorithms are used both then the Request Object is passed by value (using the request parameter) and when it is passed by reference (using the request_uri parameter).
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported,omitempty"`
//RequestObjectEncryptionAlgValuesSupported contains a list of JWE encryption algorithms (alg values) supported by the OP for Request Objects.
//These algorithms are used both when the Request Object is passed by value and by reference.
RequestObjectEncryptionAlgValuesSupported []string `json:"request_object_encryption_alg_values_supported,omitempty"`
//RequestObjectEncryptionEncValuesSupported contains a list of JWE encryption algorithms (enc values) supported by the OP for Request Objects.
//These algorithms are used both when the Request Object is passed by value and by reference.
RequestObjectEncryptionEncValuesSupported []string `json:"request_object_encryption_enc_values_supported,omitempty"`
//TokenEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Token Endpoint. If omitted, the default is client_secret_basic.
TokenEndpointAuthMethodsSupported []AuthMethod `json:"token_endpoint_auth_methods_supported,omitempty"`
//TokenEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Token Endpoint
//for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt.
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`
//RevocationEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Revocation Endpoint. If omitted, the default is client_secret_basic.
RevocationEndpointAuthMethodsSupported []AuthMethod `json:"revocation_endpoint_auth_methods_supported,omitempty"`
//RevocationEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Revocation Endpoint
//for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt.
RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"`
//IntrospectionEndpointAuthMethodsSupported contains a list of Client Authentication methods supported by the Introspection Endpoint.
IntrospectionEndpointAuthMethodsSupported []AuthMethod `json:"introspection_endpoint_auth_methods_supported,omitempty"`
//IntrospectionEndpointAuthSigningAlgValuesSupported contains a list of JWS signing algorithms (alg values) supported by the Revocation Endpoint
//for the signature of the JWT used to authenticate the Client by private_key_jwt and client_secret_jwt.
IntrospectionEndpointAuthSigningAlgValuesSupported []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"`
//DisplayValuesSupported contains a list of display parameter values that the OP supports (page, popup, touch, wap).
DisplayValuesSupported []Display `json:"display_values_supported,omitempty"`
//ClaimTypesSupported contains a list of Claim Types that the OP supports (normal, aggregated, distributed). If omitted, the default is normal Claims.
ClaimTypesSupported []string `json:"claim_types_supported,omitempty"`
//ClaimsSupported contains a list of Claim Names the OP may be able to supply values for. This list might not be exhaustive.
ClaimsSupported []string `json:"claims_supported,omitempty"`
//ClaimsParameterSupported specifies whether the OP supports use of the `claims` parameter. If omitted, the default is false.
ClaimsParameterSupported bool `json:"claims_parameter_supported,omitempty"`
//CodeChallengeMethodsSupported contains a list of Proof Key for Code Exchange (PKCE) code challenge methods supported by the OP.
CodeChallengeMethodsSupported []CodeChallengeMethod `json:"code_challenge_methods_supported,omitempty"`
//ServiceDocumentation is a URL where developers can get information about the OP and its usage.
ServiceDocumentation string `json:"service_documentation,omitempty"`
//ClaimsLocalesSupported contains a list of BCP47 language tag values that the OP supports for values of Claims returned.
ClaimsLocalesSupported []language.Tag `json:"claims_locales_supported,omitempty"`
//UILocalesSupported contains a list of BCP47 language tag values that the OP supports for the user interface.
UILocalesSupported []language.Tag `json:"ui_locales_supported,omitempty"`
//RequestParameterSupported specifies whether the OP supports use of the `request` parameter. If omitted, the default value is false.
RequestParameterSupported bool `json:"request_parameter_supported,omitempty"`
//RequestURIParameterSupported specifies whether the OP supports use of the `request_uri` parameter. If omitted, the default value is true. (therefore no omitempty)
RequestURIParameterSupported bool `json:"request_uri_parameter_supported"`
//RequireRequestURIRegistration specifies whether the OP requires any `request_uri` to be pre-registered using the request_uris registration parameter. If omitted, the default value is false.
RequireRequestURIRegistration bool `json:"require_request_uri_registration,omitempty"`
//OPPolicyURI is a URL the OP provides to the person registering the Client to read about the OP's requirements on how the RP can use the data provided by the OP.
OPPolicyURI string `json:"op_policy_uri,omitempty"`
//OPTermsOfServiceURI is a URL the OpenID Provider provides to the person registering the Client to read about OpenID Provider's terms of service.
OPTermsOfServiceURI string `json:"op_tos_uri,omitempty"`
}
type AuthMethod string

139
pkg/oidc/error.go Normal file
View file

@ -0,0 +1,139 @@
package oidc
import (
"errors"
"fmt"
)
type errorType string
const (
InvalidRequest errorType = "invalid_request"
InvalidScope errorType = "invalid_scope"
InvalidClient errorType = "invalid_client"
InvalidGrant errorType = "invalid_grant"
UnauthorizedClient errorType = "unauthorized_client"
UnsupportedGrantType errorType = "unsupported_grant_type"
ServerError errorType = "server_error"
InteractionRequired errorType = "interaction_required"
LoginRequired errorType = "login_required"
RequestNotSupported errorType = "request_not_supported"
)
var (
ErrInvalidRequest = func() *Error {
return &Error{
ErrorType: InvalidRequest,
}
}
ErrInvalidRequestRedirectURI = func() *Error {
return &Error{
ErrorType: InvalidRequest,
redirectDisabled: true,
}
}
ErrInvalidScope = func() *Error {
return &Error{
ErrorType: InvalidScope,
}
}
ErrInvalidClient = func() *Error {
return &Error{
ErrorType: InvalidClient,
}
}
ErrInvalidGrant = func() *Error {
return &Error{
ErrorType: InvalidGrant,
}
}
ErrUnauthorizedClient = func() *Error {
return &Error{
ErrorType: UnauthorizedClient,
}
}
ErrUnsupportedGrantType = func() *Error {
return &Error{
ErrorType: UnsupportedGrantType,
}
}
ErrServerError = func() *Error {
return &Error{
ErrorType: ServerError,
}
}
ErrInteractionRequired = func() *Error {
return &Error{
ErrorType: InteractionRequired,
}
}
ErrLoginRequired = func() *Error {
return &Error{
ErrorType: LoginRequired,
}
}
ErrRequestNotSupported = func() *Error {
return &Error{
ErrorType: RequestNotSupported,
}
}
)
type Error struct {
Parent error `json:"-" schema:"-"`
ErrorType errorType `json:"error" schema:"error"`
Description string `json:"error_description,omitempty" schema:"error_description,omitempty"`
State string `json:"state,omitempty" schema:"state,omitempty"`
redirectDisabled bool `schema:"-"`
}
func (e *Error) Error() string {
message := "ErrorType=" + string(e.ErrorType)
if e.Description != "" {
message += " Description=" + e.Description
}
if e.Parent != nil {
message += " Parent=" + e.Parent.Error()
}
return message
}
func (e *Error) Unwrap() error {
return e.Parent
}
func (e *Error) Is(target error) bool {
t, ok := target.(*Error)
if !ok {
return false
}
return e.ErrorType == t.ErrorType &&
(e.Description == t.Description || t.Description == "") &&
(e.State == t.State || t.State == "")
}
func (e *Error) WithParent(err error) *Error {
e.Parent = err
return e
}
func (e *Error) WithDescription(desc string, args ...interface{}) *Error {
e.Description = fmt.Sprintf(desc, args...)
return e
}
func (e *Error) IsRedirectDisabled() bool {
return e.redirectDisabled
}
// DefaultToServerError checks if the error is an Error
// if not the provided error will be wrapped into a ServerError
func DefaultToServerError(err error, description string) *Error {
oauth := new(Error)
if ok := errors.As(err, &oauth); !ok {
oauth.ErrorType = ServerError
oauth.Description = description
oauth.Parent = err
}
return oauth
}

View file

@ -42,181 +42,181 @@ type introspectionResponse struct {
claims map[string]interface{}
}
func (u *introspectionResponse) IsActive() bool {
return u.Active
func (i *introspectionResponse) IsActive() bool {
return i.Active
}
func (u *introspectionResponse) SetScopes(scope []string) {
u.Scope = scope
func (i *introspectionResponse) SetScopes(scope []string) {
i.Scope = scope
}
func (u *introspectionResponse) SetClientID(id string) {
u.ClientID = id
func (i *introspectionResponse) SetClientID(id string) {
i.ClientID = id
}
func (u *introspectionResponse) GetSubject() string {
return u.Subject
func (i *introspectionResponse) GetSubject() string {
return i.Subject
}
func (u *introspectionResponse) GetName() string {
return u.Name
func (i *introspectionResponse) GetName() string {
return i.Name
}
func (u *introspectionResponse) GetGivenName() string {
return u.GivenName
func (i *introspectionResponse) GetGivenName() string {
return i.GivenName
}
func (u *introspectionResponse) GetFamilyName() string {
return u.FamilyName
func (i *introspectionResponse) GetFamilyName() string {
return i.FamilyName
}
func (u *introspectionResponse) GetMiddleName() string {
return u.MiddleName
func (i *introspectionResponse) GetMiddleName() string {
return i.MiddleName
}
func (u *introspectionResponse) GetNickname() string {
return u.Nickname
func (i *introspectionResponse) GetNickname() string {
return i.Nickname
}
func (u *introspectionResponse) GetProfile() string {
return u.Profile
func (i *introspectionResponse) GetProfile() string {
return i.Profile
}
func (u *introspectionResponse) GetPicture() string {
return u.Picture
func (i *introspectionResponse) GetPicture() string {
return i.Picture
}
func (u *introspectionResponse) GetWebsite() string {
return u.Website
func (i *introspectionResponse) GetWebsite() string {
return i.Website
}
func (u *introspectionResponse) GetGender() Gender {
return u.Gender
func (i *introspectionResponse) GetGender() Gender {
return i.Gender
}
func (u *introspectionResponse) GetBirthdate() string {
return u.Birthdate
func (i *introspectionResponse) GetBirthdate() string {
return i.Birthdate
}
func (u *introspectionResponse) GetZoneinfo() string {
return u.Zoneinfo
func (i *introspectionResponse) GetZoneinfo() string {
return i.Zoneinfo
}
func (u *introspectionResponse) GetLocale() language.Tag {
return u.Locale
func (i *introspectionResponse) GetLocale() language.Tag {
return i.Locale
}
func (u *introspectionResponse) GetPreferredUsername() string {
return u.PreferredUsername
func (i *introspectionResponse) GetPreferredUsername() string {
return i.PreferredUsername
}
func (u *introspectionResponse) GetEmail() string {
return u.Email
func (i *introspectionResponse) GetEmail() string {
return i.Email
}
func (u *introspectionResponse) IsEmailVerified() bool {
return bool(u.EmailVerified)
func (i *introspectionResponse) IsEmailVerified() bool {
return bool(i.EmailVerified)
}
func (u *introspectionResponse) GetPhoneNumber() string {
return u.PhoneNumber
func (i *introspectionResponse) GetPhoneNumber() string {
return i.PhoneNumber
}
func (u *introspectionResponse) IsPhoneNumberVerified() bool {
return u.PhoneNumberVerified
func (i *introspectionResponse) IsPhoneNumberVerified() bool {
return i.PhoneNumberVerified
}
func (u *introspectionResponse) GetAddress() UserInfoAddress {
return u.Address
func (i *introspectionResponse) GetAddress() UserInfoAddress {
return i.Address
}
func (u *introspectionResponse) GetClaim(key string) interface{} {
return u.claims[key]
func (i *introspectionResponse) GetClaim(key string) interface{} {
return i.claims[key]
}
func (u *introspectionResponse) SetActive(active bool) {
u.Active = active
func (i *introspectionResponse) SetActive(active bool) {
i.Active = active
}
func (u *introspectionResponse) SetSubject(sub string) {
u.Subject = sub
func (i *introspectionResponse) SetSubject(sub string) {
i.Subject = sub
}
func (u *introspectionResponse) SetName(name string) {
u.Name = name
func (i *introspectionResponse) SetName(name string) {
i.Name = name
}
func (u *introspectionResponse) SetGivenName(name string) {
u.GivenName = name
func (i *introspectionResponse) SetGivenName(name string) {
i.GivenName = name
}
func (u *introspectionResponse) SetFamilyName(name string) {
u.FamilyName = name
func (i *introspectionResponse) SetFamilyName(name string) {
i.FamilyName = name
}
func (u *introspectionResponse) SetMiddleName(name string) {
u.MiddleName = name
func (i *introspectionResponse) SetMiddleName(name string) {
i.MiddleName = name
}
func (u *introspectionResponse) SetNickname(name string) {
u.Nickname = name
func (i *introspectionResponse) SetNickname(name string) {
i.Nickname = name
}
func (u *introspectionResponse) SetUpdatedAt(date time.Time) {
u.UpdatedAt = Time(date)
func (i *introspectionResponse) SetUpdatedAt(date time.Time) {
i.UpdatedAt = Time(date)
}
func (u *introspectionResponse) SetProfile(profile string) {
u.Profile = profile
func (i *introspectionResponse) SetProfile(profile string) {
i.Profile = profile
}
func (u *introspectionResponse) SetPicture(picture string) {
u.Picture = picture
func (i *introspectionResponse) SetPicture(picture string) {
i.Picture = picture
}
func (u *introspectionResponse) SetWebsite(website string) {
u.Website = website
func (i *introspectionResponse) SetWebsite(website string) {
i.Website = website
}
func (u *introspectionResponse) SetGender(gender Gender) {
u.Gender = gender
func (i *introspectionResponse) SetGender(gender Gender) {
i.Gender = gender
}
func (u *introspectionResponse) SetBirthdate(birthdate string) {
u.Birthdate = birthdate
func (i *introspectionResponse) SetBirthdate(birthdate string) {
i.Birthdate = birthdate
}
func (u *introspectionResponse) SetZoneinfo(zoneInfo string) {
u.Zoneinfo = zoneInfo
func (i *introspectionResponse) SetZoneinfo(zoneInfo string) {
i.Zoneinfo = zoneInfo
}
func (u *introspectionResponse) SetLocale(locale language.Tag) {
u.Locale = locale
func (i *introspectionResponse) SetLocale(locale language.Tag) {
i.Locale = locale
}
func (u *introspectionResponse) SetPreferredUsername(name string) {
u.PreferredUsername = name
func (i *introspectionResponse) SetPreferredUsername(name string) {
i.PreferredUsername = name
}
func (u *introspectionResponse) SetEmail(email string, verified bool) {
u.Email = email
u.EmailVerified = boolString(verified)
func (i *introspectionResponse) SetEmail(email string, verified bool) {
i.Email = email
i.EmailVerified = boolString(verified)
}
func (u *introspectionResponse) SetPhone(phone string, verified bool) {
u.PhoneNumber = phone
u.PhoneNumberVerified = verified
func (i *introspectionResponse) SetPhone(phone string, verified bool) {
i.PhoneNumber = phone
i.PhoneNumberVerified = verified
}
func (u *introspectionResponse) SetAddress(address UserInfoAddress) {
u.Address = address
func (i *introspectionResponse) SetAddress(address UserInfoAddress) {
i.Address = address
}
func (u *introspectionResponse) AppendClaims(key string, value interface{}) {
if u.claims == nil {
u.claims = make(map[string]interface{})
func (i *introspectionResponse) AppendClaims(key string, value interface{}) {
if i.claims == nil {
i.claims = make(map[string]interface{})
}
u.claims[key] = value
i.claims[key] = value
}
func (i *introspectionResponse) MarshalJSON() ([]byte, error) {

6
pkg/oidc/revocation.go Normal file
View file

@ -0,0 +1,6 @@
package oidc
type RevocationRequest struct {
Token string `schema:"token"`
TokenTypeHint string `schema:"token_type_hint"`
}

View file

@ -1,10 +1,7 @@
package oidc
import (
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"time"
@ -12,7 +9,8 @@ import (
"golang.org/x/oauth2"
"gopkg.in/square/go-jose.v2"
"github.com/caos/oidc/pkg/utils"
"github.com/caos/oidc/pkg/crypto"
"github.com/caos/oidc/pkg/http"
)
const (
@ -188,7 +186,7 @@ func (a *accessTokenClaims) MarshalJSON() ([]byte, error) {
if err != nil {
return nil, err
}
return utils.ConcatenateJSON(b, info)
return http.ConcatenateJSON(b, info)
}
func (a *accessTokenClaims) UnmarshalJSON(data []byte) error {
@ -325,7 +323,7 @@ func (t *idTokenClaims) GetSignatureAlgorithm() jose.SignatureAlgorithm {
return t.signatureAlg
}
//SetSignatureAlgorithm implements the IDTokenClaims interface
//SetAccessTokenHash implements the IDTokenClaims interface
func (t *idTokenClaims) SetAccessTokenHash(hash string) {
t.AccessTokenHash = hash
}
@ -375,7 +373,7 @@ func (t *idTokenClaims) MarshalJSON() ([]byte, error) {
if err != nil {
return nil, err
}
return utils.ConcatenateJSON(b, info)
return http.ConcatenateJSON(b, info)
}
func (t *idTokenClaims) UnmarshalJSON(data []byte) error {
@ -572,12 +570,12 @@ func NewJWTProfileAssertion(userID, keyID string, audience []string, key []byte,
}
func ClaimHash(claim string, sigAlgorithm jose.SignatureAlgorithm) (string, error) {
hash, err := utils.GetHashAlgorithm(sigAlgorithm)
hash, err := crypto.GetHashAlgorithm(sigAlgorithm)
if err != nil {
return "", err
}
return utils.HashString(hash, claim, true), nil
return crypto.HashString(hash, claim, true), nil
}
func AppendClientIDToAudience(clientID string, audience []string) []string {
@ -590,7 +588,7 @@ func AppendClientIDToAudience(clientID string, audience []string) []string {
}
func GenerateJWTProfileToken(assertion JWTProfileAssertionClaims) (string, error) {
privateKey, err := bytesToPrivateKey(assertion.GetPrivateKey())
privateKey, err := crypto.BytesToPrivateKey(assertion.GetPrivateKey())
if err != nil {
return "", err
}
@ -613,21 +611,3 @@ func GenerateJWTProfileToken(assertion JWTProfileAssertionClaims) (string, error
}
return signedAssertion.CompactSerialize()
}
func bytesToPrivateKey(priv []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(priv)
enc := x509.IsEncryptedPEMBlock(block)
b := block.Bytes
var err error
if enc {
b, err = x509.DecryptPEMBlock(block, nil)
if err != nil {
return nil, err
}
}
key, err := x509.ParsePKCS1PrivateKey(b)
if err != nil {
return nil, err
}
return key, nil
}

View file

@ -12,7 +12,7 @@ const (
//GrantTypeCode defines the grant_type `authorization_code` used for the Token Request in the Authorization Code Flow
GrantTypeCode GrantType = "authorization_code"
//GrantTypeCode defines the grant_type `refresh_token` used for the Token Request in the Refresh Token Flow
//GrantTypeRefreshToken defines the grant_type `refresh_token` used for the Token Request in the Refresh Token Flow
GrantTypeRefreshToken GrantType = "refresh_token"
//GrantTypeBearer defines the grant_type `urn:ietf:params:oauth:grant-type:jwt-bearer` used for the JWT Authorization Grant
@ -183,7 +183,7 @@ func (j *JWTTokenRequest) GetSubject() string {
return j.Subject
}
//GetSubject implements the TokenRequest interface
//GetScopes implements the TokenRequest interface
func (j *JWTTokenRequest) GetScopes() []string {
return j.Scopes
}

View file

@ -6,6 +6,7 @@ import (
"time"
"golang.org/x/text/language"
"gopkg.in/square/go-jose.v2"
)
type Audience []string
@ -66,6 +67,8 @@ type Prompt SpaceDelimitedArray
type ResponseType string
type ResponseMode string
func (s SpaceDelimitedArray) Encode() string {
return strings.Join(s, " ")
}
@ -106,3 +109,16 @@ func (t *Time) UnmarshalJSON(data []byte) error {
func (t *Time) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(*t).UTC().Unix())
}
type RequestObject struct {
Issuer string `json:"iss"`
Audience Audience `json:"aud"`
AuthRequest
}
func (r *RequestObject) GetIssuer() string {
return r.Issuer
}
func (r *RequestObject) SetSignatureAlgorithm(algorithm jose.SignatureAlgorithm) {
}

View file

@ -339,20 +339,20 @@ func NewUserInfoAddress(streetAddress, locality, region, postalCode, country, fo
}
}
func (i *userinfo) MarshalJSON() ([]byte, error) {
func (u *userinfo) MarshalJSON() ([]byte, error) {
type Alias userinfo
a := &struct {
*Alias
Locale interface{} `json:"locale,omitempty"`
UpdatedAt int64 `json:"updated_at,omitempty"`
}{
Alias: (*Alias)(i),
Alias: (*Alias)(u),
}
if !i.Locale.IsRoot() {
a.Locale = i.Locale
if !u.Locale.IsRoot() {
a.Locale = u.Locale
}
if !time.Time(i.UpdatedAt).IsZero() {
a.UpdatedAt = time.Time(i.UpdatedAt).Unix()
if !time.Time(u.UpdatedAt).IsZero() {
a.UpdatedAt = time.Time(u.UpdatedAt).Unix()
}
b, err := json.Marshal(a)
@ -360,34 +360,34 @@ func (i *userinfo) MarshalJSON() ([]byte, error) {
return nil, err
}
if len(i.claims) == 0 {
if len(u.claims) == 0 {
return b, nil
}
err = json.Unmarshal(b, &i.claims)
err = json.Unmarshal(b, &u.claims)
if err != nil {
return nil, fmt.Errorf("jws: invalid map of custom claims %v", i.claims)
return nil, fmt.Errorf("jws: invalid map of custom claims %v", u.claims)
}
return json.Marshal(i.claims)
return json.Marshal(u.claims)
}
func (i *userinfo) UnmarshalJSON(data []byte) error {
func (u *userinfo) UnmarshalJSON(data []byte) error {
type Alias userinfo
a := &struct {
Address *userInfoAddress `json:"address,omitempty"`
*Alias
UpdatedAt int64 `json:"update_at,omitempty"`
}{
Alias: (*Alias)(i),
Alias: (*Alias)(u),
}
if err := json.Unmarshal(data, &a); err != nil {
return err
}
i.Address = a.Address
i.UpdatedAt = Time(time.Unix(a.UpdatedAt, 0).UTC())
u.Address = a.Address
u.UpdatedAt = Time(time.Unix(a.UpdatedAt, 0).UTC())
if err := json.Unmarshal(data, &i.claims); err != nil {
if err := json.Unmarshal(data, &u.claims); err != nil {
return err
}

View file

@ -12,7 +12,7 @@ import (
"gopkg.in/square/go-jose.v2"
"github.com/caos/oidc/pkg/utils"
str "github.com/caos/oidc/pkg/strings"
)
type Claims interface {
@ -25,6 +25,10 @@ type Claims interface {
GetAuthenticationContextClassReference() string
GetAuthTime() time.Time
GetAuthorizedParty() string
ClaimsSignature
}
type ClaimsSignature interface {
SetSignatureAlgorithm(algorithm jose.SignatureAlgorithm)
}
@ -61,10 +65,10 @@ type Verifier interface {
type ACRVerifier func(string) error
//DefaultACRVerifier implements `ACRVerifier` returning an error
//if non of the provided values matches the acr claim
//if none of the provided values matches the acr claim
func DefaultACRVerifier(possibleValues []string) ACRVerifier {
return func(acr string) error {
if !utils.Contains(possibleValues, acr) {
if !str.Contains(possibleValues, acr) {
return fmt.Errorf("expected one of: %v, got: %q", possibleValues, acr)
}
return nil
@ -103,7 +107,7 @@ func CheckIssuer(claims Claims, issuer string) error {
}
func CheckAudience(claims Claims, clientID string) error {
if !utils.Contains(claims.GetAudience(), clientID) {
if !str.Contains(claims.GetAudience(), clientID) {
return fmt.Errorf("%w: Audience must contain client_id %q", ErrAudience, clientID)
}
@ -123,7 +127,7 @@ func CheckAuthorizedParty(claims Claims, clientID string) error {
return nil
}
func CheckSignature(ctx context.Context, token string, payload []byte, claims Claims, supportedSigAlgs []string, set KeySet) error {
func CheckSignature(ctx context.Context, token string, payload []byte, claims ClaimsSignature, supportedSigAlgs []string, set KeySet) error {
jws, err := jose.ParseSigned(token)
if err != nil {
return ErrParse
@ -138,7 +142,7 @@ func CheckSignature(ctx context.Context, token string, payload []byte, claims Cl
if len(supportedSigAlgs) == 0 {
supportedSigAlgs = []string{"RS256"}
}
if !utils.Contains(supportedSigAlgs, sig.Header.Algorithm) {
if !str.Contains(supportedSigAlgs, sig.Header.Algorithm) {
return fmt.Errorf("%w: id token signed with unsupported algorithm, expected %q got %q", ErrSignatureUnsupportedAlg, supportedSigAlgs, sig.Header.Algorithm)
}