Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit 19ab4e5

Browse files
authored
fix: update aws cognito and auth0 guides to latest security rules (#681)
1 parent e6ac9dd commit 19ab4e5

File tree

2 files changed

+103
-168
lines changed

2 files changed

+103
-168
lines changed

docs/guides/nodejs/amazon-cognito.mdx

Lines changed: 81 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ languages:
88
- typescript
99
- javascript
1010
published_at: 2023-10-09
11-
updated_at: 2024-05-15
11+
updated_at: 2024-12-11
1212
---
1313

1414
# Securing APIs with Amazon Cognito
@@ -42,14 +42,16 @@ In this new Nitric project there will be an example API with a single `GET: /hel
4242

4343
<TabItem label="TypeScript">
4444

45-
```typescript !! title:services/hello.ts
45+
```typescript title:services/api.ts
4646
import { api } from '@nitric/sdk'
4747

48-
const helloApi = api('main')
48+
const mainApi = api('main')
4949

50-
helloApi.get('/hello/:name', async (ctx) => {
50+
mainApi.get('/hello/:name', async (ctx) => {
5151
const { name } = ctx.req.params
52+
5253
ctx.res.body = `Hello ${name}`
54+
5355
return ctx
5456
})
5557
```
@@ -58,14 +60,16 @@ helloApi.get('/hello/:name', async (ctx) => {
5860

5961
<TabItem label="JavaScript">
6062

61-
```javascript !! title:services/hello.js
63+
```javascript title:services/api.js
6264
import { api } from '@nitric/sdk'
6365

64-
const helloApi = api('main')
66+
const mainApi = api('main')
6567

66-
helloApi.get('/hello/:name', async (ctx) => {
68+
mainApi.get('/hello/:name', async (ctx) => {
6769
const { name } = ctx.req.params
70+
6871
ctx.res.body = `Hello ${name}`
72+
6973
return ctx
7074
})
7175
```
@@ -78,30 +82,32 @@ helloApi.get('/hello/:name', async (ctx) => {
7882

7983
[Nitric APIs](/apis#api-security) allow initial token authentication to be performed by the API Gateways of various cloud providers, such as AWS API Gateway. When configured correctly this will ensure unauthenticated requests are rejected before reaching your application code.
8084

81-
To add this API Gateway authentication we need to create a `security definition` and then apply that definition to specific routes or the entire API. Here we'll update the `main` API by adding a new security definition named `cognito`.
85+
To add this API Gateway authentication we need to create a `security definition` and then apply that definition to specific routes or the entire API. Here we'll update the `main` API by adding a new security definition named `cognito`. This will apply to all routes in the API.
8286

8387
<Tabs syncKey="lang-node">
8488

8589
<TabItem label="TypeScript">
8690

87-
```typescript !! title:services/hello.ts
88-
import { api } from '@nitric/sdk'
91+
```typescript title:services/api.ts
92+
import { api, oidcRule } from '@nitric/sdk'
93+
94+
const defaultSecurityRule = oidcRule({
95+
name: 'cognito',
96+
issuer:
97+
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
98+
audiences: ['<app-client-id>'],
99+
})
89100

90-
const helloApi = api('main', {
91-
// declare security definition named 'cognito'.
92-
securityDefinitions: {
93-
cognito: {
94-
kind: 'jwt',
95-
issuer:
96-
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
97-
audiences: ['<app-client-id>'],
98-
},
99-
},
101+
const mainApi = api('main', {
102+
// apply the security definition to all routes in this API.
103+
security: [defaultSecurityRule()],
100104
})
101105

102-
helloApi.get('/hello/:name', async (ctx) => {
106+
mainApi.get('/hello/:name', async (ctx) => {
103107
const { name } = ctx.req.params
108+
104109
ctx.res.body = `Hello ${name}`
110+
105111
return ctx
106112
})
107113
```
@@ -110,24 +116,26 @@ helloApi.get('/hello/:name', async (ctx) => {
110116

111117
<TabItem label="JavaScript">
112118

113-
```javascript !! title:services/hello.js
114-
import { api } from '@nitric/sdk'
119+
```javascript title:services/api.js
120+
import { api, oidcRule } from '@nitric/sdk'
115121

116-
const helloApi = api('main', {
117-
// declare security definition named 'cognito'.
118-
securityDefinitions: {
119-
cognito: {
120-
kind: 'jwt',
121-
issuer:
122-
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
123-
audiences: ['<app-client-id>'],
124-
},
125-
},
122+
const defaultSecurityRule = oidcRule({
123+
name: 'cognito',
124+
issuer:
125+
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
126+
audiences: ['<app-client-id>'],
126127
})
127128

128-
helloApi.get('/hello/:name', async (ctx) => {
129+
const mainApi = api('main', {
130+
// apply the security definition to all routes in this API.
131+
security: [defaultSecurityRule()],
132+
})
133+
134+
mainApi.get('/hello/:name', async (ctx) => {
129135
const { name } = ctx.req.params
136+
130137
ctx.res.body = `Hello ${name}`
138+
131139
return ctx
132140
})
133141
```
@@ -141,74 +149,6 @@ helloApi.get('/hello/:name', async (ctx) => {
141149
values to match your values from Amazon Cognito.
142150
</Note>
143151

144-
Next, we need to ensure this security definition is applied, otherwise it won't be enforced. APIs can have multiple security definitions applied at different levels or to different routes. For example, you might have one security definition for external users and another for administrators/internal users.
145-
146-
In this example we'll apply the security at the API level, ensuring all routes require authentication.
147-
148-
<Tabs syncKey="lang-node">
149-
150-
<TabItem label="TypeScript">
151-
152-
```typescript !! title:services/hello.ts
153-
import { api } from '@nitric/sdk'
154-
155-
const helloApi = api('main', {
156-
// declare security definition named 'cognito'.
157-
securityDefinitions: {
158-
cognito: {
159-
kind: 'jwt',
160-
issuer:
161-
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
162-
audiences: ['<app-client-id>'],
163-
},
164-
},
165-
// apply the 'cognito' security definition to all routes in this API.
166-
security: {
167-
cognito: [],
168-
},
169-
})
170-
171-
helloApi.get('/hello/:name', async (ctx) => {
172-
const { name } = ctx.req.params
173-
ctx.res.body = `Hello ${name}`
174-
return ctx
175-
})
176-
```
177-
178-
</TabItem>
179-
180-
<TabItem label="JavaScript">
181-
182-
```javascript !! title:services/hello.js
183-
import { api } from '@nitric/sdk'
184-
185-
const helloApi = api('main', {
186-
// declare security definition named 'cognito'.
187-
securityDefinitions: {
188-
cognito: {
189-
kind: 'jwt',
190-
issuer:
191-
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
192-
audiences: ['<app-client-id>'],
193-
},
194-
},
195-
// apply the 'cognito' security definition to all routes in this API.
196-
security: {
197-
cognito: [],
198-
},
199-
})
200-
201-
helloApi.get('/hello/:name', async (ctx) => {
202-
const { name } = ctx.req.params
203-
ctx.res.body = `Hello ${name}`
204-
return ctx
205-
})
206-
```
207-
208-
</TabItem>
209-
210-
</Tabs>
211-
212152
<Note>
213153
It's worth noting that these security definitions are *not* enforced when
214154
testing your Nitric services locally. Currently, they're only enforced when
@@ -231,8 +171,8 @@ In the example below we're simply checking whether the user is a member of a par
231171

232172
<TabItem label="TypeScript">
233173

234-
```typescript !! title:services/hello.ts
235-
import { api, HttpContext, HttpMiddleware } from '@nitric/sdk'
174+
```typescript title:services/api.ts
175+
import { api, HttpContext, HttpMiddleware, oidcRule } from '@nitric/sdk'
236176
import * as jwt from 'jsonwebtoken'
237177

238178
interface AccessToken {
@@ -255,6 +195,8 @@ const authorizeAuthors: HttpMiddleware = (ctx: AuthContext, next) => {
255195
: ctx.req.headers['authorization']
256196
const token = authHeader?.split(' ')[1]
257197

198+
// If no token is present, deny access.
199+
// There should be a token as the security rules would be checked first, but this is a good practice.
258200
if (!token) {
259201
ctx.res.status = 401
260202
ctx.res.body = 'Unauthorized'
@@ -264,8 +206,11 @@ const authorizeAuthors: HttpMiddleware = (ctx: AuthContext, next) => {
264206
// It's valuable to validate the token's shape, skipped here for brevity.
265207
ctx.user = jwt.decode(token) as AccessToken
266208

267-
// Ensure the user is a member of the "authors" cognito group
268-
if (!ctx.user['cognito:groups'].includes('authors')) {
209+
// If the user is not a member of any groups or not an "author", deny access
210+
if (
211+
!ctx.user['cognito:groups'] ||
212+
!ctx.user['cognito:groups'].includes('authors')
213+
) {
269214
ctx.res.status = 401
270215
ctx.res.body = 'Unauthorized'
271216
return ctx
@@ -274,24 +219,19 @@ const authorizeAuthors: HttpMiddleware = (ctx: AuthContext, next) => {
274219
return next(ctx)
275220
}
276221

277-
const helloApi = api('main', {
278-
// declare security definition named 'cognito'.
279-
securityDefinitions: {
280-
cognito: {
281-
kind: 'jwt',
282-
issuer:
283-
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
284-
audiences: ['<app-client-id>'],
285-
},
286-
},
287-
// apply the 'cognito' security definition to all routes in this API.
288-
security: {
289-
cognito: [],
290-
},
222+
const defaultSecurityRule = oidcRule({
223+
name: 'cognito',
224+
issuer:
225+
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
226+
audiences: ['<app-client-id>'],
227+
})
228+
229+
const mainApi = api('main', {
230+
security: [defaultSecurityRule()],
291231
middleware: [authorizeAuthors],
292232
})
293233

294-
helloApi.get('/hello/:name', async (ctx: AuthContext) => {
234+
mainApi.get('/hello/:name', async (ctx: AuthContext) => {
295235
const { name } = ctx.req.params
296236
ctx.res.body = `Hello ${name}, you're group memberships include: ${ctx.user[
297237
'cognito:groups'
@@ -304,8 +244,8 @@ helloApi.get('/hello/:name', async (ctx: AuthContext) => {
304244

305245
<TabItem label="JavaScript">
306246

307-
```javascript !! title:services/hello.js
308-
import { api, HttpContext } from '@nitric/sdk'
247+
```javascript title:services/api.js
248+
import { api, oidcRule } from '@nitric/sdk'
309249
import * as jwt from 'jsonwebtoken'
310250

311251
/**
@@ -320,17 +260,22 @@ const authorizeAuthors = (ctx, next) => {
320260
: ctx.req.headers['authorization']
321261
const token = authHeader?.split(' ')[1]
322262

263+
// If no token is present, deny access.
264+
// There should be a token as the security rules would be checked first, but this is a good practice.
323265
if (!token) {
324266
ctx.res.status = 401
325267
ctx.res.body = 'Unauthorized'
326268
return ctx
327269
}
328270

329-
// It's valuable to check the token's shape, skipped here for brevity.
271+
// It's valuable to validate the token's shape, skipped here for brevity.
330272
ctx.user = jwt.decode(token)
331273

332-
// Ensure the user is a member of the "authors" cognito group
333-
if (!ctx.user['cognito:groups'].includes('authors')) {
274+
// If the user is not a member of any groups or not an "author", deny access
275+
if (
276+
!ctx.user['cognito:groups'] ||
277+
!ctx.user['cognito:groups'].includes('authors')
278+
) {
334279
ctx.res.status = 401
335280
ctx.res.body = 'Unauthorized'
336281
return ctx
@@ -339,24 +284,19 @@ const authorizeAuthors = (ctx, next) => {
339284
return next(ctx)
340285
}
341286

342-
const helloApi = api('main', {
343-
// declare security definition named 'cognito'.
344-
securityDefinitions: {
345-
cognito: {
346-
kind: 'jwt',
347-
issuer:
348-
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
349-
audiences: ['<app-client-id>'],
350-
},
351-
},
352-
// apply the 'cognito' security definition to all routes in this API.
353-
security: {
354-
cognito: [],
355-
},
287+
const defaultSecurityRule = oidcRule({
288+
name: 'cognito',
289+
issuer:
290+
'https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration',
291+
audiences: ['<app-client-id>'],
292+
})
293+
294+
const mainApi = api('main', {
295+
security: [defaultSecurityRule()],
356296
middleware: [authorizeAuthors],
357297
})
358298

359-
helloApi.get('/hello/:name', async (ctx) => {
299+
mainApi.get('/hello/:name', async (ctx) => {
360300
const { name } = ctx.req.params
361301
ctx.res.body = `Hello ${name}, you're group memberships include: ${ctx.user[
362302
'cognito:groups'

0 commit comments

Comments
 (0)