token, errors and more

This commit is contained in:
Livio Amstutz 2019-12-03 08:53:39 +01:00
parent 89bcd1a0c3
commit f04e7cf5b9
9 changed files with 64 additions and 24 deletions

View file

@ -2,6 +2,7 @@ package mock
import ( import (
"errors" "errors"
"time"
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
@ -32,6 +33,10 @@ func (a *AuthRequest) GetAudience() []string {
} }
} }
func (a *AuthRequest) GetAuthTime() time.Time {
return time.Now().UTC()
}
func (a *AuthRequest) GetClientID() string { func (a *AuthRequest) GetClientID() string {
return "" return ""
} }

View file

@ -163,7 +163,7 @@ func AuthResponse(authReq AuthRequest, authorizer Authorizer, w http.ResponseWri
} }
} }
idToken, err := CreateIDToken("", authReq, accessToken, time.Now(), time.Now(), "", authorizer.Signer()) idToken, err := CreateIDToken("", authReq, time.Duration(0), accessToken, authorizer.Signer())
if err != nil { if err != nil {
} }

View file

@ -2,6 +2,7 @@ package op
import ( import (
"net/http" "net/http"
"time"
"github.com/gorilla/schema" "github.com/gorilla/schema"
@ -22,6 +23,7 @@ var (
IntrospectionEndpoint: defaultIntrospectEndpoint, IntrospectionEndpoint: defaultIntrospectEndpoint,
Userinfo: defaultUserinfoEndpoint, Userinfo: defaultUserinfoEndpoint,
} }
DefaultIDTokenValidity = time.Duration(5 * time.Minute)
) )
type DefaultOP struct { type DefaultOP struct {
@ -36,7 +38,8 @@ type DefaultOP struct {
} }
type Config struct { type Config struct {
Issuer string Issuer string
IDTokenValidity time.Duration
// ScopesSupported: oidc.SupportedScopes, // ScopesSupported: oidc.SupportedScopes,
// ResponseTypesSupported: responseTypes, // ResponseTypesSupported: responseTypes,
// GrantTypesSupported: oidc.SupportedGrantTypes, // GrantTypesSupported: oidc.SupportedGrantTypes,
@ -172,6 +175,13 @@ func (p *DefaultOP) Signer() Signer {
// return // return
} }
func (p *DefaultOP) IDTokenValidity() time.Duration {
if p.config.IDTokenValidity == 0 {
p.config.IDTokenValidity = DefaultIDTokenValidity
}
return p.config.IDTokenValidity
}
// func (p *DefaultOP) ErrorHandler() func(w http.ResponseWriter, r *http.Request, authReq *oidc.AuthRequest, err error) { // func (p *DefaultOP) ErrorHandler() func(w http.ResponseWriter, r *http.Request, authReq *oidc.AuthRequest, err error) {
// return AuthRequestError // return AuthRequestError
// } // }

View file

@ -58,9 +58,11 @@ func AuthRequestError(w http.ResponseWriter, r *http.Request, authReq ErrAuthReq
func ExchangeRequestError(w http.ResponseWriter, r *http.Request, err error) { func ExchangeRequestError(w http.ResponseWriter, r *http.Request, err error) {
e, ok := err.(*OAuthError) e, ok := err.(*OAuthError)
if !ok { if !ok {
e = new(OAuthError)
e.ErrorType = ServerError e.ErrorType = ServerError
e.Description = err.Error() e.Description = err.Error()
} }
w.WriteHeader(http.StatusBadRequest)
utils.MarshalJSON(w, e) utils.MarshalJSON(w, e)
} }

View file

@ -5,6 +5,7 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/gorilla/schema" "github.com/gorilla/schema"
"gopkg.in/square/go-jose.v2"
oidc "github.com/caos/oidc/pkg/oidc" oidc "github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/pkg/op" "github.com/caos/oidc/pkg/op"
@ -69,6 +70,9 @@ type Sig struct{}
func (s *Sig) SignIDToken(*oidc.IDTokenClaims) (string, error) { func (s *Sig) SignIDToken(*oidc.IDTokenClaims) (string, error) {
return "", nil return "", nil
} }
func (s *Sig) SignatureAlgorithm() jose.SignatureAlgorithm {
return jose.HS256
}
func ExpectStorage(a op.Authorizer, t *testing.T) { func ExpectStorage(a op.Authorizer, t *testing.T) {
mockA := a.(*MockAuthorizer) mockA := a.(*MockAuthorizer)

View file

@ -10,11 +10,13 @@ import (
type Signer interface { type Signer interface {
SignIDToken(claims *oidc.IDTokenClaims) (string, error) SignIDToken(claims *oidc.IDTokenClaims) (string, error)
SignatureAlgorithm() jose.SignatureAlgorithm
} }
type idTokenSigner struct { type idTokenSigner struct {
signer jose.Signer signer jose.Signer
storage Storage storage Storage
algorithm jose.SignatureAlgorithm
} }
func NewDefaultSigner(storage Storage) (Signer, error) { func NewDefaultSigner(storage Storage) (Signer, error) {
@ -36,6 +38,7 @@ func (s *idTokenSigner) initialize() error {
if err != nil { if err != nil {
return err return err
} }
s.algorithm = key.Algorithm
return nil return nil
} }
@ -46,6 +49,7 @@ func (s *idTokenSigner) SignIDToken(claims *oidc.IDTokenClaims) (string, error)
} }
return s.Sign(payload) return s.Sign(payload)
} }
func (s *idTokenSigner) Sign(payload []byte) (string, error) { func (s *idTokenSigner) Sign(payload []byte) (string, error) {
result, err := s.signer.Sign(payload) result, err := s.signer.Sign(payload)
if err != nil { if err != nil {
@ -53,3 +57,7 @@ func (s *idTokenSigner) Sign(payload []byte) (string, error) {
} }
return result.CompactSerialize() return result.CompactSerialize()
} }
func (s *idTokenSigner) SignatureAlgorithm() jose.SignatureAlgorithm {
return s.algorithm
}

View file

@ -1,6 +1,8 @@
package op package op
import ( import (
"time"
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
"github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/oidc"
@ -22,6 +24,7 @@ type AuthRequest interface {
GetACR() string GetACR() string
GetAMR() []string GetAMR() []string
GetAudience() []string GetAudience() []string
GetAuthTime() time.Time
GetClientID() string GetClientID() string
GetNonce() string GetNonce() string
GetRedirectURI() string GetRedirectURI() string

View file

@ -5,17 +5,15 @@ import (
"net/http" "net/http"
"time" "time"
"gopkg.in/square/go-jose.v2"
"github.com/caos/oidc/pkg/utils"
"github.com/gorilla/schema" "github.com/gorilla/schema"
"github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/pkg/utils"
) )
type Exchanger interface { type Exchanger interface {
Issuer() string Issuer() string
IDTokenValidity() time.Duration
Storage() Storage Storage() Storage
Decoder() *schema.Decoder Decoder() *schema.Decoder
Signer() Signer Signer() Signer
@ -59,7 +57,7 @@ func CodeExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
ExchangeRequestError(w, r, err) ExchangeRequestError(w, r, err)
return return
} }
idToken, err := CreateIDToken(exchanger.Issuer(), authReq, "", time.Now(), time.Now(), "", exchanger.Signer()) idToken, err := CreateIDToken(exchanger.Issuer(), authReq, exchanger.IDTokenValidity(), accessToken, exchanger.Signer())
if err != nil { if err != nil {
ExchangeRequestError(w, r, err) ExchangeRequestError(w, r, err)
return return
@ -76,23 +74,23 @@ func CreateAccessToken() (string, error) {
return "accessToken", nil return "accessToken", nil
} }
func CreateIDToken(issuer string, authReq AuthRequest, sub string, exp, authTime time.Time, accessToken string, signer Signer) (string, error) { func CreateIDToken(issuer string, authReq AuthRequest, validity time.Duration, accessToken string, signer Signer) (string, error) {
var err error var err error
exp := time.Now().UTC().Add(validity)
claims := &oidc.IDTokenClaims{ claims := &oidc.IDTokenClaims{
Issuer: issuer, Issuer: issuer,
Subject: authReq.GetSubject(), Subject: authReq.GetSubject(),
Audiences: authReq.GetAudience(), Audiences: authReq.GetAudience(),
Expiration: exp, Expiration: exp,
IssuedAt: time.Now().UTC(), IssuedAt: time.Now().UTC(),
AuthTime: authTime, AuthTime: authReq.GetAuthTime(),
Nonce: authReq.GetNonce(), Nonce: authReq.GetNonce(),
AuthenticationContextClassReference: authReq.GetACR(), AuthenticationContextClassReference: authReq.GetACR(),
AuthenticationMethodsReferences: authReq.GetAMR(), AuthenticationMethodsReferences: authReq.GetAMR(),
AuthorizedParty: authReq.GetClientID(), AuthorizedParty: authReq.GetClientID(),
} }
if accessToken != "" { if accessToken != "" {
var alg jose.SignatureAlgorithm claims.AccessTokenHash, err = oidc.AccessTokenHash(accessToken, signer.SignatureAlgorithm())
claims.AccessTokenHash, err = oidc.AccessTokenHash(accessToken, alg) //TODO: alg
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -20,6 +20,12 @@ const (
stateParam = "state" stateParam = "state"
) )
var (
DefaultErrorHandler = func(w http.ResponseWriter, r *http.Request, errorType string, errorDesc string, state string) {
http.Error(w, errorType+": "+errorDesc, http.StatusInternalServerError)
}
)
//DefaultRP impements the `DelegationTokenExchangeRP` interface extending the `RelayingParty` interface //DefaultRP impements the `DelegationTokenExchangeRP` interface extending the `RelayingParty` interface
type DefaultRP struct { type DefaultRP struct {
endpoints Endpoints endpoints Endpoints
@ -30,6 +36,8 @@ type DefaultRP struct {
httpClient *http.Client httpClient *http.Client
cookieHandler *utils.CookieHandler cookieHandler *utils.CookieHandler
errorHandler func(http.ResponseWriter, *http.Request, string, string, string)
verifier Verifier verifier Verifier
} }
@ -51,6 +59,10 @@ func NewDefaultRP(rpConfig *Config, rpOpts ...DefaultRPOpts) (DelegationTokenExc
return nil, err return nil, err
} }
if p.errorHandler == nil {
p.errorHandler = DefaultErrorHandler
}
if p.verifier == nil { if p.verifier == nil {
p.verifier = NewDefaultVerifier(rpConfig.Issuer, rpConfig.ClientID, NewRemoteKeySet(p.httpClient, p.endpoints.JKWsURL)) //TODO: keys endpoint p.verifier = NewDefaultVerifier(rpConfig.Issuer, rpConfig.ClientID, NewRemoteKeySet(p.httpClient, p.endpoints.JKWsURL)) //TODO: keys endpoint
} }
@ -125,15 +137,16 @@ func (p *DefaultRP) CodeExchangeHandler(callback func(http.ResponseWriter, *http
return return
} }
params := r.URL.Query() params := r.URL.Query()
if params.Get("code") != "" { if params.Get("error") != "" {
tokens, err := p.CodeExchange(r.Context(), params.Get("code")) p.errorHandler(w, r, params.Get("error"), params.Get("error_description"), state)
if err != nil { return
http.Error(w, "failed to exchange token: "+err.Error(), http.StatusUnauthorized)
return
}
callback(w, r, tokens, state)
} }
w.Write([]byte(params.Get("error"))) tokens, err := p.CodeExchange(r.Context(), params.Get("code"))
if err != nil {
http.Error(w, "failed to exchange token: "+err.Error(), http.StatusUnauthorized)
return
}
callback(w, r, tokens, state)
} }
} }
@ -169,18 +182,15 @@ func (p *DefaultRP) DelegationTokenExchange(ctx context.Context, subjectToken st
func (p *DefaultRP) discover() error { func (p *DefaultRP) discover() error {
wellKnown := strings.TrimSuffix(p.config.Issuer, "/") + oidc.DiscoveryEndpoint wellKnown := strings.TrimSuffix(p.config.Issuer, "/") + oidc.DiscoveryEndpoint
req, err := http.NewRequest("GET", wellKnown, nil) req, err := http.NewRequest("GET", wellKnown, nil)
if err != nil { if err != nil {
return err return err
} }
discoveryConfig := new(oidc.DiscoveryConfiguration) discoveryConfig := new(oidc.DiscoveryConfiguration)
err = utils.HttpRequest(p.httpClient, req, &discoveryConfig) err = utils.HttpRequest(p.httpClient, req, &discoveryConfig)
if err != nil { if err != nil {
return err return err
} }
p.endpoints = GetEndpoints(discoveryConfig) p.endpoints = GetEndpoints(discoveryConfig)
p.oauthConfig = oauth2.Config{ p.oauthConfig = oauth2.Config{
ClientID: p.config.ClientID, ClientID: p.config.ClientID,