handle request object
This commit is contained in:
parent
72a9829117
commit
f067d723f2
7 changed files with 196 additions and 36 deletions
|
@ -59,27 +59,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
|
||||
|
|
|
@ -64,6 +64,7 @@ const (
|
|||
ServerError errorType = "server_error"
|
||||
InteractionRequired errorType = "interaction_required"
|
||||
LoginRequired errorType = "login_required"
|
||||
RequestNotSupported errorType = "request_not_supported"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -118,6 +119,11 @@ var (
|
|||
ErrorType: LoginRequired,
|
||||
}
|
||||
}
|
||||
ErrRequestNotSupported = func() *Error {
|
||||
return &Error{
|
||||
ErrorType: RequestNotSupported,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultToServerError checks if the error is an Error
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
type Audience []string
|
||||
|
@ -106,3 +107,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) {
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package op
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -41,6 +40,7 @@ type Authorizer interface {
|
|||
IDTokenHintVerifier() IDTokenHintVerifier
|
||||
Crypto() Crypto
|
||||
Issuer() string
|
||||
RequestObjectSupported() bool
|
||||
}
|
||||
|
||||
//AuthorizeValidator is an extension of Authorizer interface
|
||||
|
@ -70,6 +70,13 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) {
|
|||
AuthRequestError(w, r, authReq, err, authorizer.Encoder())
|
||||
return
|
||||
}
|
||||
if authReq.RequestParam != "" && authorizer.RequestObjectSupported() {
|
||||
authReq, err = ParseRequestObject(r.Context(), authReq, authorizer.Storage(), authorizer.Issuer())
|
||||
if err != nil {
|
||||
AuthRequestError(w, r, authReq, err, authorizer.Encoder())
|
||||
return
|
||||
}
|
||||
}
|
||||
validation := ValidateAuthRequest
|
||||
if validater, ok := authorizer.(AuthorizeValidator); ok {
|
||||
validation = validater.ValidateAuthRequest
|
||||
|
@ -79,6 +86,10 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) {
|
|||
AuthRequestError(w, r, authReq, err, authorizer.Encoder())
|
||||
return
|
||||
}
|
||||
if authReq.RequestParam != "" {
|
||||
AuthRequestError(w, r, authReq, oidc.ErrRequestNotSupported(), authorizer.Encoder())
|
||||
return
|
||||
}
|
||||
req, err := authorizer.Storage().CreateAuthRequest(r.Context(), authReq, userID)
|
||||
if err != nil {
|
||||
AuthRequestError(w, r, authReq, oidc.DefaultToServerError(err, "unable to save auth request"), authorizer.Encoder())
|
||||
|
@ -92,7 +103,7 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) {
|
|||
RedirectToLogin(req.GetID(), client, w, r)
|
||||
}
|
||||
|
||||
//ParseAuthorizeRequest parsed the http request into a oidc.AuthRequest
|
||||
//ParseAuthorizeRequest parsed the http request into an oidc.AuthRequest
|
||||
func ParseAuthorizeRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.AuthRequest, error) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
|
@ -106,6 +117,83 @@ func ParseAuthorizeRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.A
|
|||
return authReq, nil
|
||||
}
|
||||
|
||||
//ParseRequestObject parse the `request` parameter, validates the token including the signature
|
||||
//and copies the token claims into the auth request
|
||||
func ParseRequestObject(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, issuer string) (*oidc.AuthRequest, error) {
|
||||
requestObject := new(oidc.RequestObject)
|
||||
payload, err := oidc.ParseToken(authReq.RequestParam, requestObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if requestObject.ClientID != "" && requestObject.ClientID != authReq.ClientID {
|
||||
return authReq, oidc.ErrInvalidRequest()
|
||||
}
|
||||
if requestObject.ResponseType != "" && requestObject.ResponseType != authReq.ResponseType {
|
||||
return authReq, oidc.ErrInvalidRequest()
|
||||
}
|
||||
if requestObject.Issuer != requestObject.ClientID {
|
||||
return authReq, oidc.ErrInvalidRequest()
|
||||
}
|
||||
if !str.Contains(requestObject.Audience, issuer) {
|
||||
return authReq, oidc.ErrInvalidRequest()
|
||||
}
|
||||
keySet := &jwtProfileKeySet{storage, requestObject.Issuer}
|
||||
if err = oidc.CheckSignature(ctx, authReq.RequestParam, payload, requestObject, nil, keySet); err != nil {
|
||||
return authReq, err
|
||||
}
|
||||
CopyRequestObjectToAuthRequest(authReq, requestObject)
|
||||
return authReq, nil
|
||||
}
|
||||
|
||||
//CopyRequestObjectToAuthRequest overwrites present values from the Request Object into the auth request
|
||||
//and clears the `RequestParam` of the auth request
|
||||
func CopyRequestObjectToAuthRequest(authReq *oidc.AuthRequest, requestObject *oidc.RequestObject) {
|
||||
if str.Contains(authReq.Scopes, oidc.ScopeOpenID) && len(requestObject.Scopes) > 0 {
|
||||
authReq.Scopes = requestObject.Scopes
|
||||
}
|
||||
if requestObject.RedirectURI != "" {
|
||||
authReq.RedirectURI = requestObject.RedirectURI
|
||||
}
|
||||
if requestObject.State != "" {
|
||||
authReq.State = requestObject.State
|
||||
}
|
||||
if requestObject.ResponseMode != "" {
|
||||
authReq.ResponseMode = requestObject.ResponseMode
|
||||
}
|
||||
if requestObject.Nonce != "" {
|
||||
authReq.Nonce = requestObject.Nonce
|
||||
}
|
||||
if requestObject.Display != "" {
|
||||
authReq.Display = requestObject.Display
|
||||
}
|
||||
if len(requestObject.Prompt) > 0 {
|
||||
authReq.Prompt = requestObject.Prompt
|
||||
}
|
||||
if requestObject.MaxAge != nil {
|
||||
authReq.MaxAge = requestObject.MaxAge
|
||||
}
|
||||
if len(requestObject.UILocales) > 0 {
|
||||
authReq.UILocales = requestObject.UILocales
|
||||
}
|
||||
if requestObject.IDTokenHint != "" {
|
||||
authReq.IDTokenHint = requestObject.IDTokenHint
|
||||
}
|
||||
if requestObject.LoginHint != "" {
|
||||
authReq.LoginHint = requestObject.LoginHint
|
||||
}
|
||||
if len(requestObject.ACRValues) > 0 {
|
||||
authReq.ACRValues = requestObject.ACRValues
|
||||
}
|
||||
if requestObject.CodeChallenge != "" {
|
||||
authReq.CodeChallenge = requestObject.CodeChallenge
|
||||
}
|
||||
if requestObject.CodeChallengeMethod != "" {
|
||||
authReq.CodeChallengeMethod = requestObject.CodeChallengeMethod
|
||||
}
|
||||
authReq.RequestParam = ""
|
||||
}
|
||||
|
||||
//ValidateAuthRequest validates the authorize parameters and returns the userID of the id_token_hint if passed
|
||||
func ValidateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, verifier IDTokenHintVerifier) (sub string, err error) {
|
||||
authReq.MaxAge, err = ValidateAuthReqPrompt(authReq.Prompt, authReq.MaxAge)
|
||||
|
|
|
@ -22,9 +22,14 @@ type Configuration interface {
|
|||
AuthMethodPostSupported() bool
|
||||
CodeMethodS256Supported() bool
|
||||
AuthMethodPrivateKeyJWTSupported() bool
|
||||
TokenEndpointSigningAlgorithmsSupported() []string
|
||||
GrantTypeRefreshTokenSupported() bool
|
||||
GrantTypeTokenExchangeSupported() bool
|
||||
GrantTypeJWTAuthorizationSupported() bool
|
||||
IntrospectionAuthMethodPrivateKeyJWTSupported() bool
|
||||
IntrospectionEndpointSigningAlgorithmsSupported() []string
|
||||
RequestObjectSupported() bool
|
||||
RequestObjectSigningAlgorithmsSupported() []string
|
||||
|
||||
SupportedUILocales() []language.Tag
|
||||
}
|
||||
|
|
|
@ -19,23 +19,27 @@ func Discover(w http.ResponseWriter, config *oidc.DiscoveryConfiguration) {
|
|||
|
||||
func CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfiguration {
|
||||
return &oidc.DiscoveryConfiguration{
|
||||
Issuer: c.Issuer(),
|
||||
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()),
|
||||
TokenEndpoint: c.TokenEndpoint().Absolute(c.Issuer()),
|
||||
IntrospectionEndpoint: c.IntrospectionEndpoint().Absolute(c.Issuer()),
|
||||
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()),
|
||||
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(c.Issuer()),
|
||||
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
||||
ScopesSupported: Scopes(c),
|
||||
ResponseTypesSupported: ResponseTypes(c),
|
||||
GrantTypesSupported: GrantTypes(c),
|
||||
SubjectTypesSupported: SubjectTypes(c),
|
||||
IDTokenSigningAlgValuesSupported: SigAlgorithms(s),
|
||||
TokenEndpointAuthMethodsSupported: AuthMethodsTokenEndpoint(c),
|
||||
IntrospectionEndpointAuthMethodsSupported: AuthMethodsIntrospectionEndpoint(c),
|
||||
ClaimsSupported: SupportedClaims(c),
|
||||
CodeChallengeMethodsSupported: CodeChallengeMethods(c),
|
||||
UILocalesSupported: c.SupportedUILocales(),
|
||||
Issuer: c.Issuer(),
|
||||
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()),
|
||||
TokenEndpoint: c.TokenEndpoint().Absolute(c.Issuer()),
|
||||
IntrospectionEndpoint: c.IntrospectionEndpoint().Absolute(c.Issuer()),
|
||||
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()),
|
||||
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(c.Issuer()),
|
||||
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
||||
ScopesSupported: Scopes(c),
|
||||
ResponseTypesSupported: ResponseTypes(c),
|
||||
GrantTypesSupported: GrantTypes(c),
|
||||
SubjectTypesSupported: SubjectTypes(c),
|
||||
IDTokenSigningAlgValuesSupported: SigAlgorithms(s),
|
||||
RequestObjectSigningAlgValuesSupported: RequestObjectSigAlgorithms(c),
|
||||
TokenEndpointAuthMethodsSupported: AuthMethodsTokenEndpoint(c),
|
||||
TokenEndpointAuthSigningAlgValuesSupported: TokenSigAlgorithms(c),
|
||||
IntrospectionEndpointAuthSigningAlgValuesSupported: IntrospectionSigAlgorithms(c),
|
||||
IntrospectionEndpointAuthMethodsSupported: AuthMethodsIntrospectionEndpoint(c),
|
||||
ClaimsSupported: SupportedClaims(c),
|
||||
CodeChallengeMethodsSupported: CodeChallengeMethods(c),
|
||||
UILocalesSupported: c.SupportedUILocales(),
|
||||
RequestParameterSupported: c.RequestObjectSupported(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,6 +132,13 @@ func AuthMethodsTokenEndpoint(c Configuration) []oidc.AuthMethod {
|
|||
return authMethods
|
||||
}
|
||||
|
||||
func TokenSigAlgorithms(c Configuration) []string {
|
||||
if !c.AuthMethodPrivateKeyJWTSupported() {
|
||||
return nil
|
||||
}
|
||||
return c.TokenEndpointSigningAlgorithmsSupported()
|
||||
}
|
||||
|
||||
func AuthMethodsIntrospectionEndpoint(c Configuration) []oidc.AuthMethod {
|
||||
authMethods := []oidc.AuthMethod{
|
||||
oidc.AuthMethodBasic,
|
||||
|
@ -145,3 +156,17 @@ func CodeChallengeMethods(c Configuration) []oidc.CodeChallengeMethod {
|
|||
}
|
||||
return codeMethods
|
||||
}
|
||||
|
||||
func IntrospectionSigAlgorithms(c Configuration) []string {
|
||||
if !c.IntrospectionAuthMethodPrivateKeyJWTSupported() {
|
||||
return nil
|
||||
}
|
||||
return c.IntrospectionEndpointSigningAlgorithmsSupported()
|
||||
}
|
||||
|
||||
func RequestObjectSigAlgorithms(c Configuration) []string {
|
||||
if !c.RequestObjectSupported() {
|
||||
return nil
|
||||
}
|
||||
return c.RequestObjectSigningAlgorithmsSupported()
|
||||
}
|
||||
|
|
21
pkg/op/op.go
21
pkg/op/op.go
|
@ -86,6 +86,7 @@ type Config struct {
|
|||
CodeMethodS256 bool
|
||||
AuthMethodPrivateKeyJWT bool
|
||||
GrantTypeRefreshToken bool
|
||||
RequestObjectSupported bool
|
||||
SupportedUILocales []language.Tag
|
||||
}
|
||||
|
||||
|
@ -191,6 +192,10 @@ func (o *openidProvider) AuthMethodPrivateKeyJWTSupported() bool {
|
|||
return o.config.AuthMethodPrivateKeyJWT
|
||||
}
|
||||
|
||||
func (o *openidProvider) TokenEndpointSigningAlgorithmsSupported() []string {
|
||||
return []string{"RS256"}
|
||||
}
|
||||
|
||||
func (o *openidProvider) GrantTypeRefreshTokenSupported() bool {
|
||||
return o.config.GrantTypeRefreshToken
|
||||
}
|
||||
|
@ -203,6 +208,22 @@ func (o *openidProvider) GrantTypeJWTAuthorizationSupported() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (o *openidProvider) IntrospectionAuthMethodPrivateKeyJWTSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *openidProvider) IntrospectionEndpointSigningAlgorithmsSupported() []string {
|
||||
return []string{"RS256"}
|
||||
}
|
||||
|
||||
func (o *openidProvider) RequestObjectSupported() bool {
|
||||
return o.config.RequestObjectSupported
|
||||
}
|
||||
|
||||
func (o *openidProvider) RequestObjectSigningAlgorithmsSupported() []string {
|
||||
return []string{"RS256"}
|
||||
}
|
||||
|
||||
func (o *openidProvider) SupportedUILocales() []language.Tag {
|
||||
return o.config.SupportedUILocales
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue