Skip to content

Commit c79ca82

Browse files
committed
test: Update OIDC test to use real Solid-OIDC tokens
Discovered some weird behavior in a setup using Solid-OIDC and wanted to make sure we test for it.
1 parent 98174a3 commit c79ca82

4 files changed

Lines changed: 119 additions & 21 deletions

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
"@commitlint/cli": "^16.1.0",
8080
"@commitlint/config-conventional": "^16.0.0",
8181
"@comunica/bus-rdf-parse-html": "^4.4.0",
82+
"@inrupt/solid-client-authn-core": "^3.1.1",
83+
"@inrupt/solid-client-authn-node": "^3.1.1",
8284
"@types/node": "^20.19.1",
8385
"@typescript-eslint/eslint-plugin": "^5.12.1",
8486
"@typescript-eslint/parser": "^5.12.1",

test/integration/Oidc.test.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createServer, Server } from 'node:http';
66
import path from 'node:path';
77
import { getDefaultCssVariables, getPorts, instantiateFromConfig } from '../util/ServerUtil';
88
import { findTokenEndpoint, noTokenFetch, generateCredentials } from '../util/UmaUtil';
9+
import { generateCssClientCredentials, generateCssClientCredentialsToken } from '../util/Util';
910

1011
const [ cssPort, umaPort ] = getPorts('OIDC');
1112
const idpPort = umaPort + 100;
@@ -21,7 +22,7 @@ describe('A server supporting OIDC tokens', (): void => {
2122
const oidcFormat = 'http://openid.net/specs/openid-connect-core-1_0.html#IDToken';
2223

2324
beforeAll(async(): Promise<void> => {
24-
setGlobalLoggerFactory(new WinstonLoggerFactory('off'));
25+
setGlobalLoggerFactory(new WinstonLoggerFactory('info'));
2526

2627
umaApp = await instantiateFromConfig(
2728
'urn:uma:default:App',
@@ -229,8 +230,7 @@ describe('A server supporting OIDC tokens', (): void => {
229230

230231
describe('accessing a resource using a Solid OIDC token.', (): void => {
231232
const resource = `http://localhost:${cssPort}/alice/solid`;
232-
// Using dummy server so we can spoof WebID
233-
const alice = idpUrl + 'alice/profile/card#me';
233+
const alice = `http://localhost:${cssPort}/alice/profile/card#me`;
234234
const policy = `
235235
@prefix ex: <http://example.org/>.
236236
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
@@ -254,30 +254,21 @@ describe('A server supporting OIDC tokens', (): void => {
254254
expect(response.status).toBe(201);
255255
});
256256

257-
// TODO: might want a test with an actual token from the RS IDP, but would require more steps and dependencies
258257
it('can get an access token.', async(): Promise<void> => {
259258
const { as_uri, ticket } = await noTokenFetch(resource, {
260259
method: 'PUT',
261260
headers: { 'content-type': 'text/plain' },
262261
body: 'hello',
263262
});
264263
const endpoint = await findTokenEndpoint(as_uri);
265-
266-
const jwk = await importJWK(privateKey, privateKey.alg);
267-
const jwt = await new SignJWT({ webid: alice })
268-
.setSubject(alice)
269-
.setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid })
270-
.setIssuedAt()
271-
.setIssuer(idpUrl)
272-
.setAudience([ 'solid', `http://localhost:${umaPort}/uma` ])
273-
.setJti(randomUUID())
274-
.setExpirationTime(Date.now() + 5000)
275-
.sign(jwk);
264+
const credentials = await generateCssClientCredentials(
265+
`http://localhost:${cssPort}/`, 'alice@example.org', 'abc123', alice);
266+
const token = await generateCssClientCredentialsToken(`http://localhost:${cssPort}/`, credentials.id, credentials.secret);
276267

277268
const content: Record<string, string> = {
278269
grant_type: 'urn:ietf:params:oauth:grant-type:uma-ticket',
279270
ticket: ticket,
280-
claim_token: jwt,
271+
claim_token: token,
281272
claim_token_format: oidcFormat,
282273
};
283274

@@ -332,6 +323,7 @@ describe('A server supporting OIDC tokens', (): void => {
332323
});
333324
const endpoint = await findTokenEndpoint(as_uri);
334325

326+
// Not using client credentials as we can't set the client_id that way (or I forgot how to do it)
335327
const jwk = await importJWK(privateKey, privateKey.alg);
336328
const jwt = await new SignJWT({ webid: bob, azp: client })
337329
.setSubject(bob)

test/util/Util.ts

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import { KeyValueStorage } from '@solid/community-server';
2-
import { UmaConfig } from '@solidlab/uma-css';
3-
import { Mocked } from 'vitest';
4-
1+
import { joinUrl } from '@solid/community-server';
52

63
/**
74
* This is needed when you want to wait for all promises to resolve.
@@ -13,3 +10,52 @@ import { Mocked } from 'vitest';
1310
export async function flushPromises(): Promise<void> {
1411
return new Promise((await vi.importActual('timers')).setImmediate as any);
1512
}
13+
14+
/**
15+
* Registers client credentials for the given CSS account/WebID.
16+
*/
17+
export async function generateCssClientCredentials(serverUrl: string, email: string, password: string,
18+
webId: string): Promise<{ id: string, secret: string }> {
19+
let indexResponse = await fetch(joinUrl(serverUrl, '.account/'));
20+
let { controls } = await indexResponse.json() as any;
21+
22+
let response = await fetch(controls.password.login, {
23+
method: 'POST',
24+
headers: { 'content-type': 'application/json' },
25+
body: JSON.stringify({ email, password }),
26+
});
27+
const { authorization } = await response.json() as any;
28+
29+
indexResponse = await fetch(joinUrl(serverUrl, '.account/'), {
30+
headers: { authorization: `CSS-Account-Token ${authorization}` }
31+
});
32+
({ controls } = await indexResponse.json() as any);
33+
34+
response = await fetch(controls.account.clientCredentials, {
35+
method: 'POST',
36+
headers: { authorization: `CSS-Account-Token ${authorization}`, 'content-type': 'application/json' },
37+
body: JSON.stringify({ webId }),
38+
});
39+
40+
return response.json() as any;
41+
}
42+
43+
/**
44+
* Uses the generated client credentials to request an access token.
45+
*/
46+
export async function generateCssClientCredentialsToken(serverUrl: string, id: string, secret: string):
47+
Promise<string> {
48+
const authString = `${encodeURIComponent(id)}:${encodeURIComponent(secret)}`;
49+
const tokenUrl = joinUrl(serverUrl, '.oidc/token');
50+
const response = await fetch(tokenUrl, {
51+
method: 'POST',
52+
headers: {
53+
authorization: `Basic ${Buffer.from(authString).toString('base64')}`,
54+
'content-type': 'application/x-www-form-urlencoded',
55+
},
56+
body: 'grant_type=client_credentials&scope=webid',
57+
});
58+
59+
const { access_token: accessToken } = await response.json() as any;
60+
return accessToken;
61+
}

yarn.lock

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4939,6 +4939,29 @@ __metadata:
49394939
languageName: node
49404940
linkType: hard
49414941

4942+
"@inrupt/solid-client-authn-core@npm:^3.1.0, @inrupt/solid-client-authn-core@npm:^3.1.1":
4943+
version: 3.1.1
4944+
resolution: "@inrupt/solid-client-authn-core@npm:3.1.1"
4945+
dependencies:
4946+
events: "npm:^3.3.0"
4947+
jose: "npm:^5.1.3"
4948+
uuid: "npm:^11.1.0"
4949+
checksum: 10c0/837822a8df6b9bf268c6335f59859cc10331c88497b235b5c8767b5a6d74166b4972c2d4608fd956e99887d31431eddedc7ac6fdca65abfd33487c7a96c37445
4950+
languageName: node
4951+
linkType: hard
4952+
4953+
"@inrupt/solid-client-authn-node@npm:^3.1.0":
4954+
version: 3.1.1
4955+
resolution: "@inrupt/solid-client-authn-node@npm:3.1.1"
4956+
dependencies:
4957+
"@inrupt/solid-client-authn-core": "npm:^3.1.1"
4958+
jose: "npm:^5.1.3"
4959+
openid-client: "npm:^5.7.1"
4960+
uuid: "npm:^11.1.0"
4961+
checksum: 10c0/f2099322e30f00e69a682e2d37bb8f209fe61304d00d865e05e9d02ded57b4a0cb4cb867ac178213376465ca8d409730cd509dface4d798b339895a216ad440b
4962+
languageName: node
4963+
linkType: hard
4964+
49424965
"@ioredis/commands@npm:^1.1.1":
49434966
version: 1.2.0
49444967
resolution: "@ioredis/commands@npm:1.2.0"
@@ -5672,6 +5695,8 @@ __metadata:
56725695
"@commitlint/cli": "npm:^16.1.0"
56735696
"@commitlint/config-conventional": "npm:^16.0.0"
56745697
"@comunica/bus-rdf-parse-html": "npm:^4.4.0"
5698+
"@inrupt/solid-client-authn-core": "npm:^3.1.0"
5699+
"@inrupt/solid-client-authn-node": "npm:^3.1.0"
56755700
"@types/node": "npm:^20.19.1"
56765701
"@typescript-eslint/eslint-plugin": "npm:^5.12.1"
56775702
"@typescript-eslint/parser": "npm:^5.12.1"
@@ -9835,6 +9860,13 @@ __metadata:
98359860
languageName: node
98369861
linkType: hard
98379862

9863+
"jose@npm:^4.15.9":
9864+
version: 4.15.9
9865+
resolution: "jose@npm:4.15.9"
9866+
checksum: 10c0/4ed4ddf4a029db04bd167f2215f65d7245e4dc5f36d7ac3c0126aab38d66309a9e692f52df88975d99429e357e5fd8bab340ff20baab544d17684dd1d940a0f4
9867+
languageName: node
9868+
linkType: hard
9869+
98389870
"jose@npm:^4.7.0":
98399871
version: 4.15.4
98409872
resolution: "jose@npm:4.15.4"
@@ -10953,6 +10985,13 @@ __metadata:
1095310985
languageName: node
1095410986
linkType: hard
1095510987

10988+
"object-hash@npm:^2.2.0":
10989+
version: 2.2.0
10990+
resolution: "object-hash@npm:2.2.0"
10991+
checksum: 10c0/1527de843926c5442ed61f8bdddfc7dc181b6497f725b0e89fcf50a55d9c803088763ed447cac85a5aa65345f1e99c2469ba679a54349ef3c4c0aeaa396a3eb9
10992+
languageName: node
10993+
linkType: hard
10994+
1095610995
"object-inspect@npm:^1.12.2":
1095710996
version: 1.13.1
1095810997
resolution: "object-inspect@npm:1.13.1"
@@ -11020,6 +11059,13 @@ __metadata:
1102011059
languageName: node
1102111060
linkType: hard
1102211061

11062+
"oidc-token-hash@npm:^5.0.3":
11063+
version: 5.2.0
11064+
resolution: "oidc-token-hash@npm:5.2.0"
11065+
checksum: 10c0/4fa9a6f0a27c0b304aa2e4a37e433e871fb2e71418b62ada7305022ed7c70a4b325a81a4ee9661bf0c456cda0acdbeb564e68659bd6fe6a192b20bdec11688ea
11066+
languageName: node
11067+
linkType: hard
11068+
1102311069
"on-finished@npm:^2.4.1":
1102411070
version: 2.4.1
1102511071
resolution: "on-finished@npm:2.4.1"
@@ -11065,6 +11111,18 @@ __metadata:
1106511111
languageName: node
1106611112
linkType: hard
1106711113

11114+
"openid-client@npm:^5.7.1":
11115+
version: 5.7.1
11116+
resolution: "openid-client@npm:5.7.1"
11117+
dependencies:
11118+
jose: "npm:^4.15.9"
11119+
lru-cache: "npm:^6.0.0"
11120+
object-hash: "npm:^2.2.0"
11121+
oidc-token-hash: "npm:^5.0.3"
11122+
checksum: 10c0/6aae649758562002eace7574b6eda02be7eddbb0df61eef497ae98b7a4a0ae4c6b09f3f0c1b9b6cb7fcc0c70bbde2576691bf31b870db1f19ab634c1def10bc7
11123+
languageName: node
11124+
linkType: hard
11125+
1106811126
"optionator@npm:^0.9.3":
1106911127
version: 0.9.3
1107011128
resolution: "optionator@npm:0.9.3"
@@ -13580,7 +13638,7 @@ __metadata:
1358013638
languageName: node
1358113639
linkType: hard
1358213640

13583-
"uuid@npm:^11.0.0":
13641+
"uuid@npm:^11.0.0, uuid@npm:^11.1.0":
1358413642
version: 11.1.0
1358513643
resolution: "uuid@npm:11.1.0"
1358613644
bin:

0 commit comments

Comments
 (0)