Skip to content

Commit ad51c3a

Browse files
authored
Rules tests (#183)
* add rules changes to accessToken * add changeset
1 parent 746a2ab commit ad51c3a

File tree

10 files changed

+213
-40
lines changed

10 files changed

+213
-40
lines changed

.changes/fix-access-token.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
"@simulacrum/auth0-simulator": minor
3+
---
4+
Apply rules changes to the accessToken

packages/auth0/src/auth/jwt.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,3 @@ export const createJsonWebToken = (
1414
): string => {
1515
return sign(payload, privateKey, options);
1616
};
17-
18-
export function createAuthJWT(authNamespace: string, audience: string, sub: string): string {
19-
return createJsonWebToken({
20-
[`${authNamespace}`]: 'decorate token',
21-
aud: audience,
22-
iss: authNamespace,
23-
sub,
24-
});
25-
}

packages/auth0/src/handlers/auth0-handlers.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { HttpHandler, Middleware, Person, Store } from '@simulacrum/server';
2-
import type { IdTokenData, Options, QueryParams, ResponseModes } from '../types';
2+
import type { AccessTokenPayload, IdTokenData, Options, QueryParams, ResponseModes } from '../types';
33
import { createLoginRedirectHandler } from './login-redirect';
44
import { createWebMessageHandler } from './web-message';
55
import { loginView } from '../views/login';
@@ -8,7 +8,7 @@ import { stringify } from 'querystring';
88
import { decode, encode } from "base64-url";
99
import { userNamePasswordForm } from '../views/username-password';
1010
import { expiresAt } from '../auth/date';
11-
import { createAuthJWT, createJsonWebToken } from '../auth/jwt';
11+
import { createJsonWebToken } from '../auth/jwt';
1212
import { getServiceUrl } from './get-service-url';
1313
import { createRulesRunner } from '../rules/rules-runner';
1414
import type { RuleUser } from '../rules/types';
@@ -222,19 +222,24 @@ export const createAuth0Handlers = (options: Options): Record<Routes, HttpHandle
222222
idTokenData.nonce = nonce;
223223
}
224224

225-
let accessToken = {
226-
scope,
227-
};
228-
229225
let userData = {} as RuleUser;
230-
let context = { clientID, accessToken, idToken: idTokenData };
226+
let context = { clientID, accessToken: { scope }, idToken: idTokenData };
231227

232228
rulesRunner(userData, context);
233229

234-
let idToken = createJsonWebToken({ ...userData, ...context.idToken, ...context.accessToken });
230+
let idToken = createJsonWebToken({ ...userData, ...context.idToken });
231+
232+
let accessToken: AccessTokenPayload = {
233+
aud: audience,
234+
sub: idTokenData.sub,
235+
iat: idTokenData.iat,
236+
iss: idTokenData.iss,
237+
exp: idTokenData.exp,
238+
...context.accessToken
239+
};
235240

236241
res.status(200).json({
237-
access_token: createAuthJWT(url, audience, idTokenData.sub),
242+
access_token: createJsonWebToken(accessToken),
238243
id_token: idToken,
239244
expires_in: 86400,
240245
token_type: "Bearer",

packages/auth0/src/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,14 @@ export interface IdTokenData {
5252
sub: string;
5353
nonce?: string;
5454
}
55+
56+
export interface AccessTokenPayload {
57+
iss: string;
58+
sub: string;
59+
aud: string;
60+
iat: number;
61+
exp: number;
62+
scope: string;
63+
64+
[key: string]: string | number | string[];
65+
}

packages/auth0/test/auth0.test.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,6 @@ describe('Auth0 simulator', () => {
231231

232232
beforeEach(function* () {
233233
simulation = yield client.createSimulation("auth0", {
234-
options: {
235-
rulesDirectory: 'test/rules'
236-
},
237234
services: {
238235
auth0: { port: 4400 }, frontend: { port: 3000 }
239236
}
@@ -319,25 +316,6 @@ describe('Auth0 simulator', () => {
319316

320317
expect(res.status).toBe(401);
321318
});
322-
323-
it('should have ran the rules', function* () {
324-
let res: Response = yield fetch(`${authUrl}/oauth/token`, {
325-
method: 'POST',
326-
headers: {
327-
'Content-Type': 'application/json'
328-
},
329-
body: JSON.stringify({
330-
...Fields,
331-
code
332-
})
333-
});
334-
335-
let token = yield res.json();
336-
337-
let idToken = jwt.decode(token.id_token, { complete: true });
338-
339-
expect(idToken?.payload.picture).toContain('https://i.pravatar.cc');
340-
});
341319
});
342320

343321
describe('/v2/logout', () => {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2+
function addRolesAndEmail(user, context, callback) {
3+
let namespace = 'https://example.nl';
4+
let assignedRoles = ["example"];
5+
6+
let idTokenClaims = context.idToken || {};
7+
let accessTokenClaims = context.accessToken || {};
8+
9+
idTokenClaims[`${namespace}/roles`] = assignedRoles;
10+
11+
accessTokenClaims[`${namespace}/roles`] = assignedRoles;
12+
accessTokenClaims[`${namespace}/email`] = idTokenClaims.email;
13+
14+
context.idToken = idTokenClaims;
15+
context.accessToken = accessTokenClaims;
16+
17+
callback(null, user, context);
18+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"enabled": true,
3+
"order": 1,
4+
"stage": "login_success"
5+
}

packages/auth0/test/rules.test.ts

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { describe, it, beforeEach } from '@effection/mocha';
2+
import expect from 'expect';
3+
import type { Client, Simulation } from './helpers';
4+
import { createTestServer } from './helpers';
5+
import { auth0 } from '../src';
6+
import fetch from 'cross-fetch';
7+
import type { Person } from '@simulacrum/server';
8+
import { createHttpApp } from '@simulacrum/server';
9+
import { person as personScenario } from '@simulacrum/server';
10+
import jwt from 'jsonwebtoken';
11+
import { assert } from 'assert-ts';
12+
import type { Scenario } from '@simulacrum/client';
13+
14+
let Fields = {
15+
audience: "https://example.nl",
16+
client_id: "00000000000000000000000000000000",
17+
connection: "Username-Password-Authentication",
18+
nonce: "aGV6ODdFZjExbF9iMkdYZHVfQ3lYcDNVSldGRDR6dWdvREQwUms1Z0Ewaw==",
19+
redirect_uri: "http://localhost:3000",
20+
response_type: "token id_token",
21+
scope: "openid profile email offline_access",
22+
state: "sxmRf2Fq.IMN5SER~wQqbsXl5Hx0JHov",
23+
tenant: "localhost:4400",
24+
};
25+
26+
type FixtureDirectories = 'user' | 'access-token';
27+
28+
type Fixtures = `test/fixtures/rules-${FixtureDirectories}`;
29+
30+
function * createSimulation(client: Client, rulesDirectory: Fixtures) {
31+
let simulation: Simulation = yield client.createSimulation("auth0", {
32+
options: {
33+
rulesDirectory
34+
},
35+
services: {
36+
auth0: { port: 4400 }, frontend: { port: 3000 }
37+
}
38+
});
39+
40+
let authUrl = simulation.services[0].url;
41+
42+
let person: Scenario<Person> = yield client.given(simulation, "person");
43+
44+
// prime the server with the nonce field
45+
yield fetch(`${authUrl}/usernamepassword/login`, {
46+
method: 'POST',
47+
headers: {
48+
'Content-Type': 'application/json'
49+
},
50+
body: JSON.stringify({
51+
...Fields,
52+
username: person.data.email,
53+
password: person.data.password,
54+
})
55+
});
56+
57+
let res: Response = yield fetch(`https://localhost:4400/login/callback`, {
58+
method: 'POST',
59+
headers: {
60+
'Content-Type': 'application/x-www-form-urlencoded'
61+
},
62+
body: `wctx=${encodeURIComponent(JSON.stringify({
63+
...Fields
64+
}))}`
65+
});
66+
67+
let code = new URL(res.url).searchParams.get('code') as string;
68+
69+
return { code, authUrl };
70+
}
71+
72+
describe('rules', () => {
73+
let client: Client;
74+
75+
it('should', function * () {
76+
expect(true).toBe(true);
77+
});
78+
79+
beforeEach(function* () {
80+
client = yield createTestServer({
81+
simulators: {
82+
auth0: (slice, options) => {
83+
let { services } = auth0(slice, options);
84+
85+
return {
86+
services: {
87+
...services,
88+
frontend: {
89+
protocol: 'http',
90+
app: createHttpApp().get('/', function* (_, res) {
91+
res.status(200).send('ok');
92+
})
93+
},
94+
},
95+
scenarios: { person: personScenario }
96+
};
97+
}
98+
}
99+
});
100+
});
101+
102+
describe('accessToken claims', () => {
103+
let authUrl: string;
104+
let code: string;
105+
106+
beforeEach(function* () {
107+
({ authUrl, code } = yield createSimulation(client, 'test/fixtures/rules-access-token'));
108+
});
109+
110+
it('should have added the claims', function* () {
111+
let res: Response = yield fetch(`${authUrl}/oauth/token`, {
112+
method: 'POST',
113+
headers: {
114+
'Content-Type': 'application/json'
115+
},
116+
body: JSON.stringify({
117+
...Fields,
118+
code
119+
})
120+
});
121+
122+
let token = yield res.json();
123+
124+
let accessToken = jwt.decode(token.access_token, { complete: true });
125+
126+
assert(!!accessToken);
127+
128+
expect(accessToken.payload['https://example.nl/roles']).toEqual(["example"]);
129+
130+
expect(accessToken.payload['https://example.nl/email']).toEqual('paulwaters.white@yahoo.com');
131+
});
132+
});
133+
134+
describe('augment user', () => {
135+
let authUrl: string;
136+
let code: string;
137+
138+
beforeEach(function* () {
139+
({ authUrl, code } = yield createSimulation(client, 'test/fixtures/rules-user'));
140+
});
141+
142+
it('should have added a picture to the payload', function* () {
143+
let res: Response = yield fetch(`${authUrl}/oauth/token`, {
144+
method: 'POST',
145+
headers: {
146+
'Content-Type': 'application/json'
147+
},
148+
body: JSON.stringify({
149+
...Fields,
150+
code
151+
})
152+
});
153+
154+
let token = yield res.json();
155+
156+
let idToken = jwt.decode(token.id_token, { complete: true });
157+
158+
expect(idToken?.payload.picture).toContain('https://i.pravatar.cc');
159+
});
160+
});
161+
});

0 commit comments

Comments
 (0)