mirror of
https://github.com/hanzoai/openid-client.git
synced 2026-09-24 17:01:10 +00:00
148 lines
3.9 KiB
TypeScript
148 lines
3.9 KiB
TypeScript
import type QUnit from 'qunit'
|
|
|
|
import { setup } from './helper.js'
|
|
import * as client from '../src/index.js'
|
|
import * as jose from 'jose'
|
|
|
|
export default (QUnit: QUnit) => {
|
|
const { module, test } = QUnit
|
|
module('end2end-ciba.ts')
|
|
|
|
const alg = 'ES256'
|
|
|
|
for (const feat of [undefined, 'dpop', 'abort', 'encryption']) {
|
|
let dpop = false
|
|
let abort = false
|
|
let encryption = false
|
|
let title: string
|
|
switch (feat) {
|
|
case 'dpop':
|
|
title = 'end-to-end CIBA flow w/ dpop'
|
|
dpop = true
|
|
break
|
|
case 'abort':
|
|
title = 'end-to-end CIBA flow w/ abort'
|
|
abort = true
|
|
break
|
|
case 'encryption':
|
|
title = 'end-to-end CIBA flow w/ encryption'
|
|
encryption = true
|
|
break
|
|
default:
|
|
title = 'end-to-end CIBA flow'
|
|
break
|
|
}
|
|
|
|
test(title, async (t) => {
|
|
const kp = (await jose.generateKeyPair('ES256', {
|
|
extractable: true,
|
|
})) as client.CryptoKeyPair
|
|
const { metadata, issuerIdentifier, clientDecryptionKey } = await setup(
|
|
kp,
|
|
'client_secret_post',
|
|
false,
|
|
false,
|
|
false,
|
|
['refresh_token', 'urn:openid:params:grant-type:ciba'],
|
|
encryption,
|
|
)
|
|
|
|
const config = await client.discovery(
|
|
issuerIdentifier,
|
|
metadata.client_id,
|
|
metadata,
|
|
undefined,
|
|
{
|
|
execute: [
|
|
client.allowInsecureRequests,
|
|
client.enableNonRepudiationChecks,
|
|
],
|
|
},
|
|
)
|
|
|
|
if (encryption) {
|
|
client.enableDecryptingResponses(config, undefined, clientDecryptionKey)
|
|
}
|
|
|
|
const DPoP = dpop
|
|
? client.getDPoPHandle(config, await client.randomDPoPKeyPair(alg))
|
|
: undefined
|
|
|
|
const resource = 'urn:example:resource:jwt'
|
|
const params = new URLSearchParams()
|
|
params.set('resource', resource)
|
|
params.set('scope', 'openid api:write')
|
|
params.set('login_hint', 'user')
|
|
|
|
const backchannelAuthenticationResponse =
|
|
await client.initiateBackchannelAuthentication(config, params)
|
|
|
|
if (!abort) {
|
|
fetch('http://localhost:3000/ciba-sim', {
|
|
method: 'POST',
|
|
body: new URLSearchParams({
|
|
action: 'allow',
|
|
auth_req_id: backchannelAuthenticationResponse.auth_req_id,
|
|
}),
|
|
})
|
|
}
|
|
|
|
{
|
|
let signal: AbortSignal | undefined
|
|
if (abort) {
|
|
const controller = new AbortController()
|
|
signal = controller.signal
|
|
setTimeout(() => controller.abort(), 250)
|
|
}
|
|
let result: client.TokenEndpointResponse
|
|
const polling = client.pollBackchannelAuthenticationGrant(
|
|
config,
|
|
backchannelAuthenticationResponse,
|
|
undefined,
|
|
{ DPoP, signal },
|
|
)
|
|
|
|
if (abort) {
|
|
const err = await polling.catch((err) => err)
|
|
t.true(err instanceof Error)
|
|
t.propContains(err, { code: 'OAUTH_ABORT' })
|
|
t.equal(err.cause.name, 'AbortError')
|
|
return
|
|
} else {
|
|
result = await polling
|
|
}
|
|
|
|
const { access_token, token_type } = result
|
|
t.ok(access_token)
|
|
t.equal(token_type, dpop ? 'dpop' : 'bearer')
|
|
|
|
await client
|
|
.fetchProtectedResource(
|
|
config,
|
|
access_token,
|
|
new URL(config.serverMetadata().userinfo_endpoint!),
|
|
'GET',
|
|
undefined,
|
|
undefined,
|
|
{ DPoP },
|
|
)
|
|
.then(() => {
|
|
t.ok(0)
|
|
})
|
|
.catch((err) => {
|
|
if (err instanceof client.WWWAuthenticateChallengeError) {
|
|
const [{ parameters }] = err.cause
|
|
// TODO: check why the server responds with scheme bearer in dpop case
|
|
// t.equal(scheme, dpop ? 'dpop' : 'bearer')
|
|
t.equal(parameters.error, 'invalid_token')
|
|
} else {
|
|
t.ok(0)
|
|
}
|
|
})
|
|
}
|
|
|
|
t.ok(1)
|
|
})
|
|
}
|
|
}
|