Skip to content

Commit

Permalink
Replaces key discovery with well-known feature (#46)
Browse files Browse the repository at this point in the history
Closes #43
  • Loading branch information
arekkas authored Dec 19, 2017
1 parent 6519846 commit e343a61
Show file tree
Hide file tree
Showing 13 changed files with 349 additions and 13 deletions.
7 changes: 5 additions & 2 deletions director/director.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ func (d *Director) Director(r *http.Request) {
return
}

token, err := jwt.NewWithClaims(jwt.SigningMethodRS256, access.ToClaims()).SignedString(privateKey)
token := jwt.NewWithClaims(jwt.SigningMethodRS256, access.ToClaims())
token.Header["kid"] = d.KeyManager.PublicKeyID()

signed, err := token.SignedString(privateKey)
if err != nil {
d.Logger.
WithError(errors.WithStack(err)).
Expand All @@ -125,5 +128,5 @@ func (d *Director) Director(r *http.Request) {

r.URL.Scheme = d.TargetURL.Scheme
r.URL.Host = d.TargetURL.Host
*r = *r.WithContext(context.WithValue(r.Context(), requestAllowed, token))
*r = *r.WithContext(context.WithValue(r.Context(), requestAllowed, signed))
}
126 changes: 126 additions & 0 deletions docs/api.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,34 @@
},
"basePath": "/",
"paths": {
"/.well-known/jwks.json": {
"get": {
"description": "This endpoint returns public keys for validating the ID tokens issued by ORY Oathkeeper.",
"produces": [
"application/json"
],
"schemes": [
"http",
"https"
],
"summary": "Returns well known keys",
"operationId": "getWellKnown",
"responses": {
"200": {
"description": "jsonWebKeySet",
"schema": {
"$ref": "#/definitions/jsonWebKeySet"
}
},
"401": {
"$ref": "#/responses/genericError"
},
"403": {
"$ref": "#/responses/genericError"
}
}
}
},
"/rules": {
"get": {
"description": "This method returns an array of all rules that are stored in the backend. This is useful if you want to get a full\nview of what rules you have currently in place.",
Expand Down Expand Up @@ -240,6 +268,104 @@
}
},
"definitions": {
"jsonWebKey": {
"type": "object",
"properties": {
"alg": {
"description": "The \"alg\" (algorithm) parameter identifies the algorithm intended for\nuse with the key. The values used should either be registered in the\nIANA \"JSON Web Signature and Encryption Algorithms\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name.",
"type": "string",
"x-go-name": "Alg"
},
"crv": {
"type": "string",
"x-go-name": "Crv"
},
"d": {
"type": "string",
"x-go-name": "D"
},
"dp": {
"type": "string",
"x-go-name": "Dp"
},
"dq": {
"type": "string",
"x-go-name": "Dq"
},
"e": {
"type": "string",
"x-go-name": "E"
},
"k": {
"type": "string",
"x-go-name": "K"
},
"kid": {
"description": "The \"kid\" (key ID) parameter is used to match a specific key. This\nis used, for instance, to choose among a set of keys within a JWK Set\nduring key rollover. The structure of the \"kid\" value is\nunspecified. When \"kid\" values are used within a JWK Set, different\nkeys within the JWK Set SHOULD use distinct \"kid\" values. (One\nexample in which different keys might use the same \"kid\" value is if\nthey have different \"kty\" (key type) values but are considered to be\nequivalent alternatives by the application using them.) The \"kid\"\nvalue is a case-sensitive string.",
"type": "string",
"x-go-name": "Kid"
},
"kty": {
"description": "The \"kty\" (key type) parameter identifies the cryptographic algorithm\nfamily used with the key, such as \"RSA\" or \"EC\". \"kty\" values should\neither be registered in the IANA \"JSON Web Key Types\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name. The \"kty\" value is a case-sensitive string.",
"type": "string",
"x-go-name": "Kty"
},
"n": {
"type": "string",
"x-go-name": "N"
},
"p": {
"type": "string",
"x-go-name": "P"
},
"q": {
"type": "string",
"x-go-name": "Q"
},
"qi": {
"type": "string",
"x-go-name": "Qi"
},
"use": {
"description": "The \"use\" (public key use) parameter identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Values are commonly \"sig\" (signature) or \"enc\" (encryption).",
"type": "string",
"x-go-name": "Use"
},
"x": {
"type": "string",
"x-go-name": "X"
},
"x5c": {
"description": "The \"x5c\" (X.509 certificate chain) parameter contains a chain of one\nor more PKIX certificates [RFC5280]. The certificate chain is\nrepresented as a JSON array of certificate value strings. Each\nstring in the array is a base64-encoded (Section 4 of [RFC4648] --\nnot base64url-encoded) DER [ITU.X690.1994] PKIX certificate value.\nThe PKIX certificate containing the key value MUST be the first\ncertificate.",
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "X5c"
},
"y": {
"type": "string",
"x-go-name": "Y"
}
},
"x-go-name": "swaggerJSONWebKey",
"x-go-package": "github.com/ory/oathkeeper/rsakey"
},
"jsonWebKeySet": {
"type": "object",
"properties": {
"keys": {
"description": "The value of the \"keys\" parameter is an array of JWK values. By\ndefault, the order of the JWK values within the array does not imply\nan order of preference among them, although applications of JWK Sets\ncan choose to assign a meaning to the order for their purposes, if\ndesired.",
"type": "array",
"items": {
"$ref": "#/definitions/jsonWebKey"
},
"x-go-name": "Keys"
}
},
"x-go-name": "swaggerJSONWebKeySet",
"x-go-package": "github.com/ory/oathkeeper/rsakey"
},
"rule": {
"description": "A rule",
"type": "object",
Expand Down
9 changes: 7 additions & 2 deletions docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@ Authorization: bearer <jwt-signed-id-token>
```

Now, the protected resource is capable of decoding and validating the JSON Web Token using the public key supplied
by ORY Oathkeeper's API. The public key for decoding the ID token is available at Oathkeeper's management endpoint:
by ORY Oathkeeper's API. The public key for decoding the ID token is available at Oathkeeper's `/.well-known/jwks.json` endpoint:

```
http://oathkeeper:4456/keys/id-token.public
http://oathkeeper:4456/.well-known/jwks.json
```

The endpoint returns an array of valid public keys. Every ID token is equipped with a `kid` (key id) in its header. Use
the `kid` to find the appropriate key. For now, only one public key will be returned and you can simply take the first
element of the array for key validation. We recommend using a library such as [node-jwks-rsa](https://github.com/auth0/node-jwks-rsa)
for handling this appropriately.

![ID Token Transformation](images/id_token.svg)

## The ID Token
Expand Down
69 changes: 69 additions & 0 deletions rsakey/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package rsakey

// swagger:model jsonWebKeySet
type swaggerJSONWebKeySet struct {
// The value of the "keys" parameter is an array of JWK values. By
// default, the order of the JWK values within the array does not imply
// an order of preference among them, although applications of JWK Sets
// can choose to assign a meaning to the order for their purposes, if
// desired.
Keys []swaggerJSONWebKey `json:"keys"`
}

// swagger:model jsonWebKey
type swaggerJSONWebKey struct {
// The "use" (public key use) parameter identifies the intended use of
// the public key. The "use" parameter is employed to indicate whether
// a public key is used for encrypting data or verifying the signature
// on data. Values are commonly "sig" (signature) or "enc" (encryption).
Use string `json:"use,omitempty"`

// The "kty" (key type) parameter identifies the cryptographic algorithm
// family used with the key, such as "RSA" or "EC". "kty" values should
// either be registered in the IANA "JSON Web Key Types" registry
// established by [JWA] or be a value that contains a Collision-
// Resistant Name. The "kty" value is a case-sensitive string.
Kty string `json:"kty,omitempty"`

// The "kid" (key ID) parameter is used to match a specific key. This
// is used, for instance, to choose among a set of keys within a JWK Set
// during key rollover. The structure of the "kid" value is
// unspecified. When "kid" values are used within a JWK Set, different
// keys within the JWK Set SHOULD use distinct "kid" values. (One
// example in which different keys might use the same "kid" value is if
// they have different "kty" (key type) values but are considered to be
// equivalent alternatives by the application using them.) The "kid"
// value is a case-sensitive string.
Kid string `json:"kid,omitempty"`

Crv string `json:"crv,omitempty"`

// The "alg" (algorithm) parameter identifies the algorithm intended for
// use with the key. The values used should either be registered in the
// IANA "JSON Web Signature and Encryption Algorithms" registry
// established by [JWA] or be a value that contains a Collision-
// Resistant Name.
Alg string `json:"alg,omitempty"`

// The "x5c" (X.509 certificate chain) parameter contains a chain of one
// or more PKIX certificates [RFC5280]. The certificate chain is
// represented as a JSON array of certificate value strings. Each
// string in the array is a base64-encoded (Section 4 of [RFC4648] --
// not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value.
// The PKIX certificate containing the key value MUST be the first
// certificate.
X5c []string `json:"x5c,omitempty"`

K string `json:"k,omitempty"`
X string `json:"x,omitempty"`
Y string `json:"y,omitempty"`
N string `json:"n,omitempty"`
E string `json:"e,omitempty"`

D string `json:"d,omitempty"`
P string `json:"p,omitempty"`
Q string `json:"q,omitempty"`
Dp string `json:"dp,omitempty"`
Dq string `json:"dq,omitempty"`
Qi string `json:"qi,omitempty"`
}
34 changes: 25 additions & 9 deletions rsakey/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,37 @@ type Handler struct {
}

func (h *Handler) SetRoutes(r *httprouter.Router) {
r.GET("/keys/id-token.public", h.GetPublicKey)
r.GET("/.well-known/jwks.json", h.WellKnown)
}

func (h *Handler) GetPublicKey(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
// swagger:route GET /.well-known/jwks.json getWellKnown
//
// Returns well known keys
//
// This endpoint returns public keys for validating the ID tokens issued by ORY Oathkeeper.
//
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 200: jsonWebKeySet
// 401: genericError
// 403: genericError
func (h *Handler) WellKnown(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
key, err := h.M.PublicKey()
if err != nil {
h.H.WriteError(w, r, err)
return
}

jwk := &jose.JSONWebKey{
Key: key,
KeyID: "id-token.public",
Algorithm: h.M.Algorithm(),
}

h.H.Write(w, r, jwk)
h.H.Write(w, r, &jose.JSONWebKeySet{
Keys: []jose.JSONWebKey{{
Key: key,
KeyID: h.M.PublicKeyID(),
Algorithm: h.M.Algorithm(),
}},
})
}
1 change: 1 addition & 0 deletions rsakey/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ type Manager interface {
Refresh() error
PrivateKey() (*rsa.PrivateKey, error)
PublicKey() (*rsa.PublicKey, error)
PublicKeyID() string
Algorithm() string
}
4 changes: 4 additions & 0 deletions rsakey/manager_hydra.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ func (m *HydraManager) PrivateKey() (*rsa.PrivateKey, error) {
return m.key, nil
}

func (m *HydraManager) PublicKeyID() string {
return m.Set + ":public"
}

func (m *HydraManager) Algorithm() string {
return "RS256"
}
4 changes: 4 additions & 0 deletions rsakey/manager_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func (m *LocalManager) PrivateKey() (*rsa.PrivateKey, error) {
return m.key, nil
}

func (m *LocalManager) PublicKeyID() string {
return "id-token:public"
}

func (m *LocalManager) Algorithm() string {
return "RS256"
}
2 changes: 2 additions & 0 deletions sdk/go/oathkeepersdk/swagger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Class | Method | HTTP request | Description
## Documentation For Models

- [InlineResponse401](docs/InlineResponse401.md)
- [JsonWebKey](docs/JsonWebKey.md)
- [JsonWebKeySet](docs/JsonWebKeySet.md)
- [Rule](docs/Rule.md)
- [SwaggerCreateRuleParameters](docs/SwaggerCreateRuleParameters.md)
- [SwaggerGetRuleParameters](docs/SwaggerGetRuleParameters.md)
Expand Down
26 changes: 26 additions & 0 deletions sdk/go/oathkeepersdk/swagger/docs/JsonWebKey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# JsonWebKey

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**Alg** | **string** | The \&quot;alg\&quot; (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA \&quot;JSON Web Signature and Encryption Algorithms\&quot; registry established by [JWA] or be a value that contains a Collision- Resistant Name. | [optional] [default to null]
**Crv** | **string** | | [optional] [default to null]
**D** | **string** | | [optional] [default to null]
**Dp** | **string** | | [optional] [default to null]
**Dq** | **string** | | [optional] [default to null]
**E** | **string** | | [optional] [default to null]
**K** | **string** | | [optional] [default to null]
**Kid** | **string** | The \&quot;kid\&quot; (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the \&quot;kid\&quot; value is unspecified. When \&quot;kid\&quot; values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct \&quot;kid\&quot; values. (One example in which different keys might use the same \&quot;kid\&quot; value is if they have different \&quot;kty\&quot; (key type) values but are considered to be equivalent alternatives by the application using them.) The \&quot;kid\&quot; value is a case-sensitive string. | [optional] [default to null]
**Kty** | **string** | The \&quot;kty\&quot; (key type) parameter identifies the cryptographic algorithm family used with the key, such as \&quot;RSA\&quot; or \&quot;EC\&quot;. \&quot;kty\&quot; values should either be registered in the IANA \&quot;JSON Web Key Types\&quot; registry established by [JWA] or be a value that contains a Collision- Resistant Name. The \&quot;kty\&quot; value is a case-sensitive string. | [optional] [default to null]
**N** | **string** | | [optional] [default to null]
**P** | **string** | | [optional] [default to null]
**Q** | **string** | | [optional] [default to null]
**Qi** | **string** | | [optional] [default to null]
**Use** | **string** | The \&quot;use\&quot; (public key use) parameter identifies the intended use of the public key. The \&quot;use\&quot; parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly \&quot;sig\&quot; (signature) or \&quot;enc\&quot; (encryption). | [optional] [default to null]
**X** | **string** | | [optional] [default to null]
**X5c** | **[]string** | The \&quot;x5c\&quot; (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate. | [optional] [default to null]
**Y** | **string** | | [optional] [default to null]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


10 changes: 10 additions & 0 deletions sdk/go/oathkeepersdk/swagger/docs/JsonWebKeySet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# JsonWebKeySet

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**Keys** | [**[]JsonWebKey**](jsonWebKey.md) | The value of the \&quot;keys\&quot; parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired. | [optional] [default to null]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


Loading

0 comments on commit e343a61

Please sign in to comment.