change verifier interfaces
This commit is contained in:
parent
3777f1436d
commit
143ff3482c
11 changed files with 274 additions and 179 deletions
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/rp"
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
||||
|
@ -18,7 +17,7 @@ type Authorizer interface {
|
|||
Decoder() utils.Decoder
|
||||
Encoder() utils.Encoder
|
||||
Signer() Signer
|
||||
IDTokenVerifier() rp.Verifier
|
||||
IDTokenVerifier() IDTokenHintVerifier
|
||||
Crypto() Crypto
|
||||
Issuer() string
|
||||
}
|
||||
|
@ -27,10 +26,10 @@ type Authorizer interface {
|
|||
//implementing it's own validation mechanism for the auth request
|
||||
type AuthorizeValidator interface {
|
||||
Authorizer
|
||||
ValidateAuthRequest(context.Context, *oidc.AuthRequest, Storage, rp.Verifier) (string, error)
|
||||
ValidateAuthRequest(context.Context, *oidc.AuthRequest, Storage, IDTokenHintVerifier) (string, error)
|
||||
}
|
||||
|
||||
//ValidationAuthorizer is an extension of Authorizer interface
|
||||
//ValidationAuthorizer is an extension of Authorizer interface
|
||||
//implementing it's own validation mechanism for the auth request
|
||||
//
|
||||
//Deprecated: ValidationAuthorizer exists for historical compatibility. Use ValidationAuthorizer itself
|
||||
|
@ -78,6 +77,7 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) {
|
|||
RedirectToLogin(req.GetID(), client, w, r)
|
||||
}
|
||||
|
||||
//ParseAuthorizeRequest parsed the http request into a AuthRequest
|
||||
func ParseAuthorizeRequest(r *http.Request, decoder utils.Decoder) (*oidc.AuthRequest, error) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
|
@ -91,7 +91,8 @@ func ParseAuthorizeRequest(r *http.Request, decoder utils.Decoder) (*oidc.AuthRe
|
|||
return authReq, nil
|
||||
}
|
||||
|
||||
func ValidateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, verifier rp.Verifier) (string, error) {
|
||||
//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) (string, error) {
|
||||
client, err := storage.GetClientByClientID(ctx, authReq.ClientID)
|
||||
if err != nil {
|
||||
return "", ErrServerError(err.Error())
|
||||
|
@ -108,6 +109,7 @@ func ValidateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, storage
|
|||
return ValidateAuthReqIDTokenHint(ctx, authReq.IDTokenHint, verifier)
|
||||
}
|
||||
|
||||
//ValidateAuthReqScopes validates the passed scopes
|
||||
func ValidateAuthReqScopes(scopes []string) error {
|
||||
if len(scopes) == 0 {
|
||||
return ErrInvalidRequest("The scope of your request is missing. Please ensure some scopes are requested. If you have any questions, you may contact the administrator of the application.")
|
||||
|
@ -118,6 +120,7 @@ func ValidateAuthReqScopes(scopes []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
//ValidateAuthReqRedirectURI validates the passed redirect_uri and response_type to the registered uris and client type
|
||||
func ValidateAuthReqRedirectURI(client Client, uri string, responseType oidc.ResponseType) error {
|
||||
if uri == "" {
|
||||
return ErrInvalidRequestRedirectURI("The redirect_uri is missing in the request. Please ensure it is added to the request. If you have any questions, you may contact the administrator of the application.")
|
||||
|
@ -150,6 +153,7 @@ func ValidateAuthReqRedirectURI(client Client, uri string, responseType oidc.Res
|
|||
return nil
|
||||
}
|
||||
|
||||
//ValidateAuthReqResponseType validates the passed response_type to the registered response types
|
||||
func ValidateAuthReqResponseType(client Client, responseType oidc.ResponseType) error {
|
||||
if responseType == "" {
|
||||
return ErrInvalidRequest("The response type is missing in your request. If you have any questions, you may contact the administrator of the application.")
|
||||
|
@ -160,7 +164,9 @@ func ValidateAuthReqResponseType(client Client, responseType oidc.ResponseType)
|
|||
return nil
|
||||
}
|
||||
|
||||
func ValidateAuthReqIDTokenHint(ctx context.Context, idTokenHint string, verifier rp.Verifier) (string, error) {
|
||||
//ValidateAuthReqIDTokenHint validates the id_token_hint (if passed as parameter in the request)
|
||||
//and returns the `sub` claim
|
||||
func ValidateAuthReqIDTokenHint(ctx context.Context, idTokenHint string, verifier IDTokenHintVerifier) (string, error) {
|
||||
if idTokenHint == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
@ -171,11 +177,13 @@ func ValidateAuthReqIDTokenHint(ctx context.Context, idTokenHint string, verifie
|
|||
return claims.Subject, nil
|
||||
}
|
||||
|
||||
//RedirectToLogin redirects the end user to the Login UI for authentication
|
||||
func RedirectToLogin(authReqID string, client Client, w http.ResponseWriter, r *http.Request) {
|
||||
login := client.LoginURL(authReqID)
|
||||
http.Redirect(w, r, login, http.StatusFound)
|
||||
}
|
||||
|
||||
//AuthorizeCallback handles the callback after authentication in the Login UI
|
||||
func AuthorizeCallback(w http.ResponseWriter, r *http.Request, authorizer Authorizer) {
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
|
@ -192,6 +200,7 @@ func AuthorizeCallback(w http.ResponseWriter, r *http.Request, authorizer Author
|
|||
AuthResponse(authReq, authorizer, w, r)
|
||||
}
|
||||
|
||||
//AuthResponse creates the successful authentication response (either code or tokens)
|
||||
func AuthResponse(authReq AuthRequest, authorizer Authorizer, w http.ResponseWriter, r *http.Request) {
|
||||
client, err := authorizer.Storage().GetClientByClientID(r.Context(), authReq.GetClientID())
|
||||
if err != nil {
|
||||
|
@ -205,6 +214,7 @@ func AuthResponse(authReq AuthRequest, authorizer Authorizer, w http.ResponseWri
|
|||
return
|
||||
}
|
||||
|
||||
//AuthResponseCode creates the successful code authentication response
|
||||
func AuthResponseCode(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer) {
|
||||
code, err := CreateAuthRequestCode(r.Context(), authReq, authorizer.Storage(), authorizer.Crypto())
|
||||
if err != nil {
|
||||
|
@ -218,6 +228,7 @@ func AuthResponseCode(w http.ResponseWriter, r *http.Request, authReq AuthReques
|
|||
http.Redirect(w, r, callback, http.StatusFound)
|
||||
}
|
||||
|
||||
//AuthResponseToken creates the successful token(s) authentication response
|
||||
func AuthResponseToken(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer, client Client) {
|
||||
createAccessToken := authReq.GetResponseType() != oidc.ResponseTypeIDTokenOnly
|
||||
resp, err := CreateTokenResponse(r.Context(), authReq, client, authorizer, createAccessToken, "")
|
||||
|
@ -234,6 +245,7 @@ func AuthResponseToken(w http.ResponseWriter, r *http.Request, authReq AuthReque
|
|||
http.Redirect(w, r, callback, http.StatusFound)
|
||||
}
|
||||
|
||||
//CreateAuthRequestCode creates and stores a code for the auth code response
|
||||
func CreateAuthRequestCode(ctx context.Context, authReq AuthRequest, storage Storage, crypto Crypto) (string, error) {
|
||||
code, err := BuildAuthRequestCode(authReq, crypto)
|
||||
if err != nil {
|
||||
|
|
|
@ -47,7 +47,7 @@ type DefaultOP struct {
|
|||
endpoints *endpoints
|
||||
storage Storage
|
||||
signer Signer
|
||||
verifier rp.Verifier
|
||||
verifier IDTokenHintVerifier
|
||||
crypto Crypto
|
||||
http http.Handler
|
||||
decoder *schema.Decoder
|
||||
|
@ -184,7 +184,7 @@ func NewDefaultOP(ctx context.Context, config *Config, storage Storage, opOpts .
|
|||
p.signer = NewDefaultSigner(ctx, storage, keyCh)
|
||||
go p.ensureKey(ctx, storage, keyCh, p.timer)
|
||||
|
||||
p.verifier = rp.NewDefaultVerifier(config.Issuer, "", p, rp.WithIgnoreAudience(), rp.WithIgnoreExpiration())
|
||||
p.verifier = NewIDTokenHintVerifier(config.Issuer, p)
|
||||
|
||||
p.http = CreateRouter(p, p.interceptors...)
|
||||
|
||||
|
@ -238,10 +238,6 @@ func (p *DefaultOP) HandleDiscovery(w http.ResponseWriter, r *http.Request) {
|
|||
Discover(w, CreateDiscoveryConfig(p, p.Signer()))
|
||||
}
|
||||
|
||||
func (p *DefaultOP) Probes() []ProbesFn {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *DefaultOP) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
|
||||
keyID := ""
|
||||
for _, sig := range jws.Signatures {
|
||||
|
@ -279,7 +275,7 @@ func (p *DefaultOP) Crypto() Crypto {
|
|||
return p.crypto
|
||||
}
|
||||
|
||||
func (p *DefaultOP) ClientJWTVerifier() rp.Verifier {
|
||||
func (p *DefaultOP) ClientJWTVerifier() oidc.Verifier {
|
||||
return p.verifier
|
||||
}
|
||||
|
||||
|
@ -330,7 +326,7 @@ func (p *DefaultOP) HandleEndSession(w http.ResponseWriter, r *http.Request) {
|
|||
func (p *DefaultOP) DefaultLogoutRedirectURI() string {
|
||||
return p.config.DefaultLogoutRedirectURI
|
||||
}
|
||||
func (p *DefaultOP) IDTokenVerifier() rp.Verifier {
|
||||
func (p *DefaultOP) IDTokenVerifier() IDTokenHintVerifier {
|
||||
return p.verifier
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,11 @@ const (
|
|||
|
||||
type OpenIDProvider interface {
|
||||
Configuration
|
||||
HandleKeys(w http.ResponseWriter, r *http.Request)
|
||||
HttpHandler() http.Handler
|
||||
Authorizer
|
||||
SessionEnder
|
||||
Signer() Signer
|
||||
Probes() []ProbesFn
|
||||
HttpHandler() http.Handler
|
||||
}
|
||||
|
||||
type HttpInterceptor func(http.Handler) http.Handler
|
||||
|
|
|
@ -5,14 +5,13 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/rp"
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
||||
type SessionEnder interface {
|
||||
Decoder() utils.Decoder
|
||||
Storage() Storage
|
||||
IDTokenVerifier() rp.Verifier
|
||||
IDTokenVerifier() IDTokenHintVerifier
|
||||
DefaultLogoutRedirectURI() string
|
||||
}
|
||||
|
||||
|
@ -63,7 +62,7 @@ func ValidateEndSessionRequest(ctx context.Context, req *oidc.EndSessionRequest,
|
|||
if req.IdTokenHint == "" {
|
||||
return session, nil
|
||||
}
|
||||
claims, err := ender.IDTokenVerifier().VerifyIDToken(ctx, req.IdTokenHint)
|
||||
claims, err := VerifyIDTokenHint(ctx, req.IdTokenHint, ender.IDTokenVerifier())
|
||||
if err != nil {
|
||||
return nil, ErrInvalidRequest("id_token_hint invalid")
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ type Exchanger interface {
|
|||
|
||||
type VerifyExchanger interface {
|
||||
Exchanger
|
||||
ClientJWTVerifier() rp.Verifier
|
||||
ClientJWTVerifier() oidc.Verifier
|
||||
}
|
||||
|
||||
func tokenHandler(exchanger Exchanger) func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -34,7 +34,8 @@ func tokenHandler(exchanger Exchanger) func(w http.ResponseWriter, r *http.Reque
|
|||
CodeExchange(w, r, exchanger)
|
||||
return
|
||||
case string(oidc.GrantTypeBearer):
|
||||
JWTExchange(w, r, exchanger)
|
||||
ex, _ := exchanger.(VerifyExchanger)
|
||||
JWTExchange(w, r, ex)
|
||||
return
|
||||
case "excahnge":
|
||||
TokenExchange(w, r, exchanger)
|
||||
|
@ -161,23 +162,6 @@ func (c ClientJWTVerifier) ClientID() string {
|
|||
return c.issuer
|
||||
}
|
||||
|
||||
func (c ClientJWTVerifier) SupportedSignAlgs() []string {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (c ClientJWTVerifier) KeySet() oidc.KeySet {
|
||||
// return c.claims
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c ClientJWTVerifier) ACR() oidc.ACRVerifier {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (c ClientJWTVerifier) MaxAge() time.Duration {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (c ClientJWTVerifier) MaxAgeIAT() time.Duration {
|
||||
//TODO: define in conf/opts
|
||||
return 1 * time.Hour
|
||||
|
@ -224,15 +208,15 @@ func VerifyJWTAssertion(ctx context.Context, assertion string, exchanger Exchang
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckAudience(verifier.claims.GetAudience(), verifier); err != nil {
|
||||
if err = oidc.CheckAudience(verifier.claims, verifier.issuer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckExpiration(verifier.claims.GetExpiration(), verifier); err != nil {
|
||||
if err = oidc.CheckExpiration(verifier.claims, verifier.Offset()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckIssuedAt(verifier.claims.GetIssuedAt(), verifier); err != nil {
|
||||
if err = oidc.CheckIssuedAt(verifier.claims, verifier.MaxAgeIAT(), verifier.Offset()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,66 @@ package op
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
)
|
||||
|
||||
type IDTokenHintVerifier interface {
|
||||
oidc.Verifier
|
||||
SupportedSignAlgs() []string
|
||||
KeySet() oidc.KeySet
|
||||
ACR() oidc.ACRVerifier
|
||||
MaxAge() time.Duration
|
||||
}
|
||||
|
||||
//VerifyIDToken validates the id token according to
|
||||
type idTokenHintVerifier struct {
|
||||
issuer string
|
||||
maxAgeIAT time.Duration
|
||||
offset time.Duration
|
||||
supportedSignAlgs []string
|
||||
maxAge time.Duration
|
||||
acr oidc.ACRVerifier
|
||||
keySet oidc.KeySet
|
||||
}
|
||||
|
||||
func (i *idTokenHintVerifier) Issuer() string {
|
||||
return i.issuer
|
||||
}
|
||||
|
||||
func (i *idTokenHintVerifier) MaxAgeIAT() time.Duration {
|
||||
return i.maxAgeIAT
|
||||
}
|
||||
|
||||
func (i *idTokenHintVerifier) Offset() time.Duration {
|
||||
return i.offset
|
||||
}
|
||||
|
||||
func (i *idTokenHintVerifier) SupportedSignAlgs() []string {
|
||||
return i.supportedSignAlgs
|
||||
}
|
||||
|
||||
func (i *idTokenHintVerifier) KeySet() oidc.KeySet {
|
||||
return i.keySet
|
||||
}
|
||||
|
||||
func (i *idTokenHintVerifier) ACR() oidc.ACRVerifier {
|
||||
return i.acr
|
||||
}
|
||||
|
||||
func (i *idTokenHintVerifier) MaxAge() time.Duration {
|
||||
return i.maxAge
|
||||
}
|
||||
|
||||
func NewIDTokenHintVerifier(issuer string, keySet oidc.KeySet) IDTokenHintVerifier {
|
||||
verifier := &idTokenHintVerifier{
|
||||
issuer: issuer,
|
||||
keySet: keySet,
|
||||
}
|
||||
return verifier
|
||||
}
|
||||
|
||||
//VerifyIDTokenHint validates the id token according to
|
||||
//https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||
func VerifyIDTokenHint(ctx context.Context, token string, v IDTokenHintVerifier) (*oidc.IDTokenClaims, error) {
|
||||
claims := new(oidc.IDTokenClaims)
|
||||
|
@ -22,51 +74,28 @@ func VerifyIDTokenHint(ctx context.Context, token string, v IDTokenHintVerifier)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//2, check issuer (exact match)
|
||||
if err := oidc.CheckIssuer(claims.GetIssuer(), v); err != nil {
|
||||
|
||||
if err := oidc.CheckIssuer(claims, v.Issuer()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//3. check aud (aud must contain client_id, all aud strings must be allowed)
|
||||
if err = oidc.CheckAudience(claims.GetAudience(), v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckAuthorizedParty(claims.GetAudience(), claims.GetAuthorizedParty(), v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//6. check signature by keys
|
||||
//7. check alg default is rs256
|
||||
//8. check if alg is mac based (hs...) -> audience contains client_id. for validation use utf-8 representation of your client_secret
|
||||
if err = oidc.CheckSignature(ctx, decrypted, payload, claims, v.SupportedSignAlgs(), v.KeySet()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//9. check exp before now
|
||||
if err = oidc.CheckExpiration(claims.GetExpiration(), v); err != nil {
|
||||
if err = oidc.CheckExpiration(claims, v.Offset()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//10. check iat duration is optional (can be checked)
|
||||
if err = oidc.CheckIssuedAt(claims.GetIssuedAt(), v); err != nil {
|
||||
if err = oidc.CheckIssuedAt(claims, v.MaxAgeIAT(), v.Offset()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*
|
||||
//11. check nonce (check if optional possible) id_token.nonce == sentNonce
|
||||
if err = oidc.CheckNonce(claims.GetNonce()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*/
|
||||
|
||||
//12. if acr requested check acr
|
||||
if err = oidc.CheckAuthorizationContextClassReference(claims.GetAuthenticationContextClassReference(), v); err != nil {
|
||||
if err = oidc.CheckAuthorizationContextClassReference(claims, v.ACR()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//13. if auth_time requested check if auth_time is less than max age
|
||||
if err = oidc.CheckAuthTime(claims.GetAuthTime(), v); err != nil {
|
||||
if err = oidc.CheckAuthTime(claims, v.MaxAge()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return claims, nil
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue