Commit 898a2094 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Requestor can now configure client connect timeout, result jwt expiry

parent 1131c29d
...@@ -95,12 +95,14 @@ type ServerJwt struct { ...@@ -95,12 +95,14 @@ type ServerJwt struct {
type RequestorBaseRequest struct { type RequestorBaseRequest struct {
ResultJwtValidity int `json:"validity"` // Validity of session result JWT in seconds ResultJwtValidity int `json:"validity"` // Validity of session result JWT in seconds
ClientTimeout int `json:"timeout"` // Wait this many seconds for the IRMA app to connect before the session times out
CallbackUrl string `json:"callbackUrl"` // URL to post session result to CallbackUrl string `json:"callbackUrl"` // URL to post session result to
} }
type RequestorRequest interface { type RequestorRequest interface {
Validator Validator
SessionRequest() SessionRequest SessionRequest() SessionRequest
Base() RequestorBaseRequest
} }
// A ServiceProviderRequest contains a disclosure request. // A ServiceProviderRequest contains a disclosure request.
...@@ -172,6 +174,18 @@ func (r *IdentityProviderRequest) SessionRequest() SessionRequest { ...@@ -172,6 +174,18 @@ func (r *IdentityProviderRequest) SessionRequest() SessionRequest {
return r.Request return r.Request
} }
func (r *ServiceProviderRequest) Base() RequestorBaseRequest {
return r.RequestorBaseRequest
}
func (r *SignatureRequestorRequest) Base() RequestorBaseRequest {
return r.RequestorBaseRequest
}
func (r *IdentityProviderRequest) Base() RequestorBaseRequest {
return r.RequestorBaseRequest
}
// SessionRequest is an IRMA session. // SessionRequest is an IRMA session.
type SessionRequest interface { type SessionRequest interface {
Validator Validator
...@@ -624,7 +638,7 @@ func (claims *SignatureRequestorJwt) Action() Action { return ActionSigning } ...@@ -624,7 +638,7 @@ func (claims *SignatureRequestorJwt) Action() Action { return ActionSigning }
func (claims *IdentityProviderJwt) Action() Action { return ActionIssuing } func (claims *IdentityProviderJwt) Action() Action { return ActionIssuing }
func SignedRequestorJwt(request SessionRequest, alg jwt.SigningMethod, key interface{}, name string) (string, error) { func SignSessionRequest(request SessionRequest, alg jwt.SigningMethod, key interface{}, name string) (string, error) {
var jwtcontents RequestorJwt var jwtcontents RequestorJwt
switch r := request.(type) { switch r := request.(type) {
case *IssuanceRequest: case *IssuanceRequest:
...@@ -636,3 +650,19 @@ func SignedRequestorJwt(request SessionRequest, alg jwt.SigningMethod, key inter ...@@ -636,3 +650,19 @@ func SignedRequestorJwt(request SessionRequest, alg jwt.SigningMethod, key inter
} }
return jwtcontents.Sign(alg, key) return jwtcontents.Sign(alg, key)
} }
func SignRequestorRequest(request RequestorRequest, alg jwt.SigningMethod, key interface{}, name string) (string, error) {
var jwtcontents RequestorJwt
switch r := request.(type) {
case *IdentityProviderRequest:
jwtcontents = NewIdentityProviderJwt(name, nil)
jwtcontents.(*IdentityProviderJwt).Request = r
case *ServiceProviderRequest:
jwtcontents = NewServiceProviderJwt(name, nil)
jwtcontents.(*ServiceProviderJwt).Request = r
case *SignatureRequestorRequest:
jwtcontents = NewSignatureRequestorJwt(name, nil)
jwtcontents.(*SignatureRequestorJwt).Request = r
}
return jwtcontents.Sign(alg, key)
}
...@@ -84,7 +84,13 @@ func (s memorySessionStore) deleteExpired() { ...@@ -84,7 +84,13 @@ func (s memorySessionStore) deleteExpired() {
expired := make([]string, 0, len(s.m)) expired := make([]string, 0, len(s.m))
for token, session := range s.m { for token, session := range s.m {
session.Lock() session.Lock()
if session.lastActive.Add(5 * time.Minute).Before(time.Now()) {
timeout := 5 * time.Minute
if session.status == server.StatusInitialized && session.rrequest.Base().ClientTimeout != 0 {
timeout = time.Duration(session.rrequest.Base().ClientTimeout) * time.Second
}
if session.lastActive.Add(timeout).Before(time.Now()) {
if !session.finished() { if !session.finished() {
conf.Logger.Infof("Session %s expired", token) conf.Logger.Infof("Session %s expired", token)
session.markAlive() session.markAlive()
......
...@@ -206,7 +206,7 @@ func jwtAuthenticate( ...@@ -206,7 +206,7 @@ func jwtAuthenticate(
if !claims.VerifyIssuedAt(time.Now().Unix(), true) { if !claims.VerifyIssuedAt(time.Now().Unix(), true) {
return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "jwt not yet valid") return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "jwt not yet valid")
} }
if time.Unix(claims.IssuedAt, 0).Add(10 * time.Minute).Before(time.Now()) { // TODO make configurable if time.Unix(claims.IssuedAt, 0).Add(time.Duration(conf.MaxRequestAge) * time.Second).Before(time.Now()) {
return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "jwt too old") return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "jwt too old")
} }
......
...@@ -47,6 +47,9 @@ type Configuration struct { ...@@ -47,6 +47,9 @@ type Configuration struct {
// Private key to sign result JWTs with. If absent, /result-jwt and /getproof are disabled. // Private key to sign result JWTs with. If absent, /result-jwt and /getproof are disabled.
JwtPrivateKey string `json:"jwtprivatekey" mapstructure:"jwtprivatekey"` JwtPrivateKey string `json:"jwtprivatekey" mapstructure:"jwtprivatekey"`
// Max age in seconds of a session request JWT (using iat field)
MaxRequestAge int `json:"maxrequestage" mapstructure:"maxrequestage"`
Verbose int `json:"verbose" mapstructure:"verbose"` Verbose int `json:"verbose" mapstructure:"verbose"`
Quiet bool `json:"quiet" mapstructure:"quiet"` Quiet bool `json:"quiet" mapstructure:"quiet"`
......
...@@ -75,6 +75,7 @@ func setFlags(cmd *cobra.Command) error { ...@@ -75,6 +75,7 @@ func setFlags(cmd *cobra.Command) error {
flags.String("cachepath", cachepath, "Directory for writing cache files to") flags.String("cachepath", cachepath, "Directory for writing cache files to")
flags.StringP("jwtissuer", "j", "irmaserver", "JWT issuer") flags.StringP("jwtissuer", "j", "irmaserver", "JWT issuer")
flags.StringP("jwtprivatekey", "w", "", "JWT private key or path to it") flags.StringP("jwtprivatekey", "w", "", "JWT private key or path to it")
flags.Int("maxrequestage", 300, "Max age in seconds of a session request JWT")
flags.StringP("url", "u", defaulturl, "External URL to server to which the IRMA client connects") flags.StringP("url", "u", defaulturl, "External URL to server to which the IRMA client connects")
flags.StringP("listenaddr", "l", "0.0.0.0", "Address at which to listen") flags.StringP("listenaddr", "l", "0.0.0.0", "Address at which to listen")
flags.IntP("port", "p", 8088, "Port at which to listen") flags.IntP("port", "p", 8088, "Port at which to listen")
...@@ -148,6 +149,7 @@ func configure() error { ...@@ -148,6 +149,7 @@ func configure() error {
GlobalPermissions: irmaserver.Permissions{}, GlobalPermissions: irmaserver.Permissions{},
JwtIssuer: viper.GetString("jwtissuer"), JwtIssuer: viper.GetString("jwtissuer"),
JwtPrivateKey: viper.GetString("jwtprivatekey"), JwtPrivateKey: viper.GetString("jwtprivatekey"),
MaxRequestAge: viper.GetInt("maxrequestage"),
Verbose: viper.GetInt("verbose"), Verbose: viper.GetInt("verbose"),
Quiet: viper.GetBool("quiet"), Quiet: viper.GetBool("quiet"),
} }
......
...@@ -227,7 +227,8 @@ func handleJwtResult(w http.ResponseWriter, r *http.Request) { ...@@ -227,7 +227,8 @@ func handleJwtResult(w http.ResponseWriter, r *http.Request) {
return return
} }
res := irmarequestor.GetSessionResult(chi.URLParam(r, "token")) sessiontoken := chi.URLParam(r, "token")
res := irmarequestor.GetSessionResult(sessiontoken)
if res == nil { if res == nil {
server.WriteError(w, server.ErrorSessionUnknown, "") server.WriteError(w, server.ErrorSessionUnknown, "")
return return
...@@ -242,6 +243,10 @@ func handleJwtResult(w http.ResponseWriter, r *http.Request) { ...@@ -242,6 +243,10 @@ func handleJwtResult(w http.ResponseWriter, r *http.Request) {
claims.Issuer = conf.JwtIssuer claims.Issuer = conf.JwtIssuer
claims.IssuedAt = time.Now().Unix() claims.IssuedAt = time.Now().Unix()
claims.Subject = string(res.Type) + "_result" claims.Subject = string(res.Type) + "_result"
validity := irmarequestor.GetRequest(sessiontoken).Base().ResultJwtValidity
if validity != 0 {
claims.ExpiresAt = time.Now().Unix() + int64(validity)
}
// Sign the jwt and return it // Sign the jwt and return it
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
...@@ -262,7 +267,8 @@ func handleJwtProofs(w http.ResponseWriter, r *http.Request) { ...@@ -262,7 +267,8 @@ func handleJwtProofs(w http.ResponseWriter, r *http.Request) {
return return
} }
res := irmarequestor.GetSessionResult(chi.URLParam(r, "token")) sessiontoken := chi.URLParam(r, "token")
res := irmarequestor.GetSessionResult(sessiontoken)
if res == nil { if res == nil {
server.WriteError(w, server.ErrorSessionUnknown, "") server.WriteError(w, server.ErrorSessionUnknown, "")
return return
...@@ -287,6 +293,10 @@ func handleJwtProofs(w http.ResponseWriter, r *http.Request) { ...@@ -287,6 +293,10 @@ func handleJwtProofs(w http.ResponseWriter, r *http.Request) {
claims["iss"] = conf.JwtIssuer claims["iss"] = conf.JwtIssuer
} }
claims["status"] = res.Status claims["status"] = res.Status
validity := irmarequestor.GetRequest(sessiontoken).Base().ResultJwtValidity
if validity != 0 {
claims["exp"] = time.Now().Unix() + int64(validity)
}
// Disclosed credentials and possibly signature // Disclosed credentials and possibly signature
m := make(map[irma.AttributeTypeIdentifier]string, len(res.Disclosed)) m := make(map[irma.AttributeTypeIdentifier]string, len(res.Disclosed))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment