@@ -7,7 +7,7 @@ import path from 'node:path';
77import { getDefaultCssVariables , getPorts , instantiateFromConfig } from '../util/ServerUtil' ;
88import { findTokenEndpoint , noTokenFetch } from '../util/UmaUtil' ;
99
10- const [ cssPort , umaPort ] = getPorts ( 'Policies ' ) ;
10+ const [ cssPort , umaPort ] = getPorts ( 'OIDC ' ) ;
1111const idpPort = umaPort + 100 ;
1212
1313describe ( 'A server supporting OIDC tokens' , ( ) : void => {
@@ -47,7 +47,6 @@ describe('A server supporting OIDC tokens', (): void => {
4747 privateKey = { ...await generator . getPrivateKey ( ) , kid : 'kid' } ;
4848 const publicKey = { ...await generator . getPublicKey ( ) , kid : 'kid' }
4949 idp = createServer ( ( req , res ) => {
50- console . log ( req . url ) ;
5150 if ( req . url ! . endsWith ( '/card' ) ) {
5251 res . writeHead ( 200 , { 'content-type' : 'text/turtle' } ) ;
5352 res . end ( `
@@ -64,6 +63,12 @@ describe('A server supporting OIDC tokens', (): void => {
6463 return ;
6564 }
6665 res . writeHead ( 200 , { 'content-type' : 'application/json' } ) ;
66+ if ( req . url ! . endsWith ( '/client' ) ) {
67+ res . end ( JSON . stringify ( {
68+ '@context' : [ 'https://www.w3.org/ns/solid/oidc-context.jsonld' ] ,
69+ } ) ) ;
70+ return ;
71+ }
6772 if ( req . url ! . endsWith ( '/.well-known/openid-configuration' ) ) {
6873 res . end ( JSON . stringify ( { jwks_uri : idpUrl } ) ) ;
6974 return ;
@@ -136,9 +141,76 @@ describe('A server supporting OIDC tokens', (): void => {
136141 } ) ;
137142 } ) ;
138143
144+ describe ( 'accessing a resource using a standard OIDC token with a specific client.' , ( ) : void => {
145+ const resource = `http://localhost:${ cssPort } /alice/standardClient` ;
146+ const sub = '123456' ;
147+ const client = 'my-client' ;
148+ const policy = `
149+ @prefix ex: <http://example.org/>.
150+ @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
151+ @prefix dct: <http://purl.org/dc/terms/>.
152+ ex:policyStandardClient a odrl:Set;
153+ odrl:uid ex:policyStandardClient ;
154+ odrl:permission ex:permissionStandardClient .
155+
156+ ex:permissionStandardClient a odrl:Permission ;
157+ odrl:assignee <${ sub } > ;
158+ odrl:assigner <${ webId } > ;
159+ odrl:action odrl:read , odrl:create , odrl:modify ;
160+ odrl:target <http://localhost:${ cssPort } /alice/> ;
161+ odrl:constraint ex:constraintStandardClient.
162+
163+ ex:constraintStandardClient
164+ odrl:leftOperand odrl:purpose ;
165+ odrl:operator odrl:eq ;
166+ odrl:rightOperand <${ client } > .` ;
167+
168+ it ( 'can set up the policy.' , async ( ) : Promise < void > => {
169+ const response = await fetch ( policyEndpoint , {
170+ method : 'POST' ,
171+ headers : { authorization : webId , 'content-type' : 'text/turtle' } ,
172+ body : policy ,
173+ } ) ;
174+ expect ( response . status ) . toBe ( 201 ) ;
175+ } ) ;
176+
177+ it ( 'can get an access token.' , async ( ) : Promise < void > => {
178+ const { as_uri, ticket } = await noTokenFetch ( resource , {
179+ method : 'PUT' ,
180+ headers : { 'content-type' : 'text/plain' } ,
181+ body : 'hello' ,
182+ } ) ;
183+ const endpoint = await findTokenEndpoint ( as_uri ) ;
184+
185+ // TODO: also add token that fails
186+ const jwk = await importJWK ( privateKey , privateKey . alg ) ;
187+ const jwt = await new SignJWT ( { azp : client } )
188+ . setSubject ( sub )
189+ . setProtectedHeader ( { alg : privateKey . alg , kid : privateKey . kid } )
190+ . setIssuedAt ( )
191+ . setIssuer ( idpUrl )
192+ . setAudience ( `http://localhost:${ umaPort } /uma` )
193+ . setJti ( randomUUID ( ) )
194+ . sign ( jwk ) ;
195+
196+ const content : Record < string , string > = {
197+ grant_type : 'urn:ietf:params:oauth:grant-type:uma-ticket' ,
198+ ticket : ticket ,
199+ claim_token : jwt ,
200+ claim_token_format : oidcFormat ,
201+ } ;
202+
203+ const response = await fetch ( endpoint , {
204+ method : 'POST' ,
205+ headers : { 'content-type' : 'application/json' } ,
206+ body : JSON . stringify ( content ) ,
207+ } ) ;
208+ expect ( response . status ) . toBe ( 200 ) ;
209+ } ) ;
210+ } ) ;
139211
140212 describe ( 'accessing a resource using a Solid OIDC token.' , ( ) : void => {
141- const resource = `http://localhost:${ cssPort } /alice/standard ` ;
213+ const resource = `http://localhost:${ cssPort } /alice/solid ` ;
142214 // Using dummy server so we can spoof WebID
143215 const alice = idpUrl + 'alice/profile/card#me' ;
144216 const policy = `
@@ -199,4 +271,73 @@ describe('A server supporting OIDC tokens', (): void => {
199271 expect ( response . status ) . toBe ( 200 ) ;
200272 } ) ;
201273 } ) ;
274+
275+ describe ( 'accessing a resource using a Solid OIDC token with a specific client.' , ( ) : void => {
276+ const resource = `http://localhost:${ cssPort } /bob/solidClient` ;
277+ // Using dummy server so we can spoof WebID
278+ const bob = idpUrl + 'bob/profile/card#me' ;
279+ const client = idpUrl + 'client' ;
280+ const policy = `
281+ @prefix ex: <http://example.org/>.
282+ @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
283+ @prefix dct: <http://purl.org/dc/terms/>.
284+ ex:policySolidClient a odrl:Set;
285+ odrl:uid ex:policySolidClient ;
286+ odrl:permission ex:permissionSolidClient .
287+
288+ ex:permissionSolidClient a odrl:Permission ;
289+ odrl:assignee <${ bob } > ;
290+ odrl:assigner <${ webId } > ;
291+ odrl:action odrl:read , odrl:create , odrl:modify ;
292+ odrl:target <http://localhost:${ cssPort } /bob/> ;
293+ odrl:constraint ex:constraintSolidClient.
294+
295+ ex:constraintSolidClient
296+ odrl:leftOperand odrl:purpose ;
297+ odrl:operator odrl:eq ;
298+ odrl:rightOperand <${ client } > .` ;
299+
300+ it ( 'can set up the policy.' , async ( ) : Promise < void > => {
301+ const response = await fetch ( policyEndpoint , {
302+ method : 'POST' ,
303+ headers : { authorization : webId , 'content-type' : 'text/turtle' } ,
304+ body : policy ,
305+ } ) ;
306+ expect ( response . status ) . toBe ( 201 ) ;
307+ } ) ;
308+
309+ it ( 'can get an access token.' , async ( ) : Promise < void > => {
310+ const { as_uri, ticket } = await noTokenFetch ( resource , {
311+ method : 'PUT' ,
312+ headers : { 'content-type' : 'text/plain' } ,
313+ body : 'hello' ,
314+ } ) ;
315+ const endpoint = await findTokenEndpoint ( as_uri ) ;
316+
317+ const jwk = await importJWK ( privateKey , privateKey . alg ) ;
318+ const jwt = await new SignJWT ( { webid : bob , azp : client } )
319+ . setSubject ( bob )
320+ . setProtectedHeader ( { alg : privateKey . alg , kid : privateKey . kid } )
321+ . setIssuedAt ( )
322+ . setIssuer ( idpUrl )
323+ . setAudience ( [ 'solid' , `http://localhost:${ umaPort } /uma` ] )
324+ . setJti ( randomUUID ( ) )
325+ . setExpirationTime ( Date . now ( ) + 5000 )
326+ . sign ( jwk ) ;
327+
328+ const content : Record < string , string > = {
329+ grant_type : 'urn:ietf:params:oauth:grant-type:uma-ticket' ,
330+ ticket : ticket ,
331+ claim_token : jwt ,
332+ claim_token_format : oidcFormat ,
333+ } ;
334+
335+ const response = await fetch ( endpoint , {
336+ method : 'POST' ,
337+ headers : { 'content-type' : 'application/json' } ,
338+ body : JSON . stringify ( content ) ,
339+ } ) ;
340+ expect ( response . status ) . toBe ( 200 ) ;
341+ } ) ;
342+ } ) ;
202343} ) ;
0 commit comments