/
ext-oauth-middleware.md
241 lines (214 loc) · 9.25 KB
/
ext-oauth-middleware.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
---
date: 2017-03-23T16:06:42Z
title: External OAuth Middleware
tags: ["OAuth 2.0", "Security", "External Middleware", "Introspection"]
description: "How to use external middleware with OAuth and Tyk"
menu:
main:
parent: "OAuth 2.0"
weight: 7
---
## Introduction
Tyk offers two types of OAuth authentication flow; Tyk itself as the identity provider (IdP) and Tyk connecting to an external 3rd party IdP. ‘External OAuth’ is what we call this second mechanism. To call an API that is protected by OAuth, you need to have an access token from the third party IDP (it could be an opaque token or a JWT).
For subsequent calls the access token is provided alongside the API call and needs to be validated. With JWT, Tyk can confirm the validity of the JWT with the secret provided in your config. The secret signs the JWT when created and confirms that none of its contents has changed.
For this reason, information like the expiry date which are often set within the JWT cannot be changed after the JWT has been initially created and signed. This means you are not able to revoke a token before the expiry set in the JWT with the standard JWT flow. With OAuth you can use [OAuth introspection](https://www.rfc-editor.org/rfc/rfc7662) to overcome this. With introspection, you can validate the access token via an introspection endpoint that validates the token.
Let’s see how external OAuth middleware is configured.
### OAS contract
```yaml
externalOAuthServer:
enabled: true,
providers: # only one item in the array for now (we're going to support just one IDP config in the first iteration)
- jwt: #validate JWTs generated by 3rd party Oauth servers (like Okta)
enabled: true
signingMethod: HMAC/RSA/ECDSA # to verify signing method used in jwt
source: key # secret to verify signature
issuedAtValidationSkew: 0
notBeforeValidationSkew: 0
expiresAtValidationSkew: 0
identityBaseField: # identity claimName
introspection: # array for introspection details
enabled: true/false
clientID: # for introspection request
clientSecret: # for introspection request, if empty will use oAuth.secret
url: # token introspection endpoint
cache: # Tyk will cache the introspection response when `cache.enabled` is set to `true`
enabled: true/false,
timeout: 0 # The duration (in seconds) for which Tyk will retain the introspection outcome in its cache. If the value is "0", it indicates that the introspection outcome will be stored in the cache until the token's expiration.
identityBaseField: # identity claimName
```
### Tyk Classic API definition contract
```yaml
"external_oauth": {
"enabled": true,
"providers": [
{
"jwt": {
"enabled": false,
"signing_method": rsa/ecdsa/hmac,
"source": # jwk url/ base64 encoded static secret / base64 encoded jwk url
"identity_base_field": # identity claim name
"expires_at_validation_skew": # validation skew config for exp
"not_before_validation_skew": # validation skew config for nbf
"issued_at_validation_skew" : # validation skew config for iat
},
"introspection": {
"enabled": true,
"url": # introspection endpoint url
"client_id": # client id used for introspection
"client_secret": # client secret to be filled here (plain text for now, TODO: decide on a more secure mechanism)
"identity_base_field": # identity claim name
"cache": {
"enabled": true,
"timeout": # timeout in seconds
}
}
}
]
}
```
- `externalOAuthServer` set `enabled` to `true` to enable the middleware.
- `providers` is an array of multiple IDP configurations, with each IDP config being an element in the `providers` array.
- You can use this config to use JWT self validation using `jwt` or use introspection via `instropection` in the `providers` section .
{{< note success >}}
**Note**
For now, you’ll be limiting `providers` to have only one element, ie one IDP configured.
{{< /note >}}
### JWT
There could be cases when you don’t need to introspect a JWT access token from a third party IDP, and instead you can just validate the JWT. This is similar to existing JWT middleware, adding it in External OAuth middleware for semantic reasons.
- `enabled` - enables JWT validation.
- `signingMethod` - specifies the signing method used to sign the JWT.
- `source` - the secret source, it can be one of:
- a base64 encoded static secret
- a valid JWK url in plain text
- a valid JWK url in base64 encoded format
- `issuedAtValidationSkew` , `notBeforeValidationSkew`, `expiresAtValidationSkew` can be used to [configure clock skew]({{< ref "/content/basic-config-and-security/security/authentication-authorization/json-web-tokens.md#jwt-clock-skew-configuration" >}}) for json web token validation.
- `identityBaseField` - the identity key name for claims. If empty it will default to `sub`.
### Example: Tyk OAS API definition with JWT validation enabled
```json
"securitySchemes": {
"external_jwt": {
"enabled": true,
"header": {
"enabled": true,
"name": "Authorization"
},
"providers": [
{
"jwt": {
"enabled": true,
"signingMethod": "hmac",
"source": "dHlrLTEyMw==",
"identityBaseField": "sub"
}
}
]
}
}
```
### Example: Tyk Classic API definition with JWT validation enabled
```json
"external_oauth": {
"enabled": true,
"providers": [
{
"jwt": {
"enabled": true,
"signing_method": "hmac",
"source": "dHlrLTEyMw==",
"issued_at_validation_skew": 0,
"not_before_validation_skew": 0,
"expires_at_validation_skew": 0,
"identity_base_field": "sub"
},
"introspection": {
"enabled": false,
"url": "",
"client_id": "",
"client_secret": "",
"identity_base_field": "",
"cache": {
"enabled": false,
"timeout": 0
}
}
}
]
}
```
## Introspection
For cases where you need to introspect the OAuth access token, Tyk uses the information in the `provider.introspection` section of the contract. This makes a network call to the configured introspection endpoint with the provided `clientID` and `clientSecret` to introspect the access token.
- `enabled` - enables OAuth introspection
- `clientID` - clientID used for OAuth introspection, available from IDP
- `clientSecret` - secret used to authenticate introspection call, available from IDP
- `url` - endpoint URL to make the introspection call
- `identityBaseField` - the identity key name for claims. If empty it will default to `sub`.
### Caching
Introspection via a third party IdP is a network call. Sometimes it may be inefficient to call the introspection endpoint every time an API is called. Caching is the solution for this situation. Tyk caches the introspection response when `enabled` is set to `true` inside the `cache` configuration of `introspection`. Then it retrieves the value from the cache until the `timeout` value finishes. However, there is a trade-off here. When the timeout is long, it may result in accessing the upstream with a revoked access token. When it is short, the cache is not used as much resulting in more network calls.
The recommended way to handle this balance is to never set the `timeout` value beyond the expiration time of the token, which would have been returned in the `exp` parameter of the introspection response.
See the example introspection cache configuration:
```yaml
"introspection": {
...
"cache": {
"enabled": true,
"timeout": 60 // in seconds
}
}
```
### Example: Tyk OAS API definition external OAuth introspection enabled
```json
"securitySchemes": {
"keycloak_oauth": {
"enabled": true,
"header": {
"enabled": true,
"name": "Authorization"
},
"providers": [
{
"introspection": {
"enabled": true,
"url": "http://localhost:8080/realms/tyk/protocol/openid-connect/token/introspect",
"clientId": "introspection-client",
"clientSecret": "DKyFN0WXu7IXWzR05QZOnnSnK8uAAZ3U",
"identityBaseField": "sub",
"cache": {
"enabled": true,
"timeout": 3
}
}
}
]
}
}
```
### Example: Tyk Classic API definition with external OAuth introspection enabled
```json
"external_oauth": {
"enabled": true,
"providers": [
{
"jwt": {
"enabled": false,
"signing_method": "",
"source": "",
"issued_at_validation_skew": 0,
"not_before_validation_skew": 0,
"expires_at_validation_skew": 0,
"identity_base_field": ""
},
"introspection": {
"enabled": true,
"url": "http://localhost:8080/realms/tyk/protocol/openid-connect/token/introspect",
"client_id": "introspection-client",
"client_secret": "DKyFN0WXu7IXWzR05QZOnnSnK8uAAZ3U",
"identity_base_field": "sub",
"cache": {
"enabled": true,
"timeout": 3
}
}
}
]
}
```