mirror of
https://github.com/hanzoai/openid-client.git
synced 2026-09-24 17:01:10 +00:00
444 lines
10 KiB
TypeScript
444 lines
10 KiB
TypeScript
import test from 'ava'
|
|
import * as client from '../src/index.js'
|
|
import * as oauth from 'oauth4webapi'
|
|
import * as undici from 'undici'
|
|
import * as jose from 'jose'
|
|
|
|
test('dpop may be used with DCR w/ initial access token', async (t) => {
|
|
let agent = new undici.MockAgent()
|
|
agent.disableNetConnect()
|
|
|
|
const issuer = new URL('https://op.example.com')
|
|
const kp = await client.randomDPoPKeyPair('ES256')
|
|
|
|
const mockAgent = agent.get(issuer.origin)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'GET',
|
|
path: '/.well-known/openid-configuration',
|
|
})
|
|
.reply(200, {
|
|
issuer: issuer.href,
|
|
registration_endpoint: `${issuer.origin}/reg`,
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
headers({ dpop, authorization }) {
|
|
return typeof dpop === 'string' && authorization === 'DPoP token'
|
|
},
|
|
})
|
|
.reply(
|
|
400,
|
|
{},
|
|
{
|
|
headers: {
|
|
'dpop-nonce': 'use this',
|
|
'www-authenticate': 'dpop error="use_dpop_nonce"',
|
|
},
|
|
},
|
|
)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
headers({ dpop, authorization }) {
|
|
const { nonce } = jose.decodeJwt(dpop)
|
|
return nonce === 'use this' && authorization === 'DPoP token'
|
|
},
|
|
})
|
|
.reply(201, {
|
|
client_id: 'foo',
|
|
})
|
|
|
|
await client.dynamicClientRegistration(issuer, {}, undefined, {
|
|
initialAccessToken: 'token',
|
|
DPoP: oauth.DPoP({}, kp),
|
|
// @ts-ignore
|
|
[client.customFetch](url, options) {
|
|
return undici.fetch(url, { ...options, dispatcher: agent })
|
|
},
|
|
})
|
|
|
|
t.notThrows(() => agent.assertNoPendingInterceptors())
|
|
})
|
|
|
|
test('dpop may be used with DCR w/o initial access token', async (t) => {
|
|
let agent = new undici.MockAgent()
|
|
agent.disableNetConnect()
|
|
|
|
const issuer = new URL('https://op.example.com')
|
|
const kp = await client.randomDPoPKeyPair('ES256')
|
|
|
|
const mockAgent = agent.get(issuer.origin)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'GET',
|
|
path: '/.well-known/openid-configuration',
|
|
})
|
|
.reply(200, {
|
|
issuer: issuer.href,
|
|
registration_endpoint: `${issuer.origin}/reg`,
|
|
token_endpoint: `${issuer.origin}/token`,
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
headers({ dpop, authorization }) {
|
|
return typeof dpop === 'string' && authorization === undefined
|
|
},
|
|
})
|
|
.reply(
|
|
400,
|
|
{},
|
|
{
|
|
headers: {
|
|
'dpop-nonce': 'use this',
|
|
'www-authenticate': 'dpop error="use_dpop_nonce"',
|
|
},
|
|
},
|
|
)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
headers({ dpop, authorization }) {
|
|
const { nonce } = jose.decodeJwt(dpop)
|
|
return nonce === 'use this' && authorization === undefined
|
|
},
|
|
})
|
|
.reply(201, {
|
|
client_id: 'foo',
|
|
client_secret: 'bar',
|
|
client_secret_expires_at: 0,
|
|
})
|
|
|
|
await client.dynamicClientRegistration(issuer, {}, undefined, {
|
|
DPoP: oauth.DPoP({}, kp),
|
|
// @ts-ignore
|
|
[client.customFetch](url, options) {
|
|
return undici.fetch(url, { ...options, dispatcher: agent })
|
|
},
|
|
})
|
|
|
|
t.notThrows(() => agent.assertNoPendingInterceptors())
|
|
})
|
|
|
|
test('DCR uses client_secret_post by default when client_secret was returned', async (t) => {
|
|
let agent = new undici.MockAgent()
|
|
agent.disableNetConnect()
|
|
|
|
const issuer = new URL('https://op.example.com')
|
|
|
|
const mockAgent = agent.get(issuer.origin)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'GET',
|
|
path: '/.well-known/openid-configuration',
|
|
})
|
|
.reply(200, {
|
|
issuer: issuer.href,
|
|
registration_endpoint: `${issuer.origin}/reg`,
|
|
token_endpoint: `${issuer.origin}/token`,
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
})
|
|
.reply(201, {
|
|
client_id: 'foo',
|
|
client_secret: 'bar',
|
|
client_secret_expires_at: 0,
|
|
})
|
|
|
|
const config = await client.dynamicClientRegistration(issuer, {}, undefined, {
|
|
// @ts-ignore
|
|
[client.customFetch](url, options) {
|
|
return undici.fetch(url, { ...options, dispatcher: agent })
|
|
},
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/token',
|
|
body(body) {
|
|
const params = new URLSearchParams(body)
|
|
t.true(params.has('client_id'))
|
|
t.true(params.has('client_secret'))
|
|
return true
|
|
},
|
|
})
|
|
.reply(200, {
|
|
access_token: 'token',
|
|
token_type: 'bearer',
|
|
})
|
|
.times(2)
|
|
|
|
await client.clientCredentialsGrant(config)
|
|
await client.clientCredentialsGrant(config)
|
|
|
|
t.notThrows(() => agent.assertNoPendingInterceptors())
|
|
})
|
|
|
|
test('DCR uses none by default when client_secret was not returned', async (t) => {
|
|
let agent = new undici.MockAgent()
|
|
agent.disableNetConnect()
|
|
|
|
const issuer = new URL('https://op.example.com')
|
|
|
|
const mockAgent = agent.get(issuer.origin)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'GET',
|
|
path: '/.well-known/openid-configuration',
|
|
})
|
|
.reply(200, {
|
|
issuer: issuer.href,
|
|
registration_endpoint: `${issuer.origin}/reg`,
|
|
token_endpoint: `${issuer.origin}/token`,
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
})
|
|
.reply(201, {
|
|
client_id: 'foo',
|
|
})
|
|
|
|
const config = await client.dynamicClientRegistration(issuer, {}, undefined, {
|
|
// @ts-ignore
|
|
[client.customFetch](url, options) {
|
|
return undici.fetch(url, { ...options, dispatcher: agent })
|
|
},
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/token',
|
|
body(body) {
|
|
const params = new URLSearchParams(body)
|
|
t.true(params.has('client_id'))
|
|
return true
|
|
},
|
|
})
|
|
.reply(200, {
|
|
access_token: 'token',
|
|
token_type: 'bearer',
|
|
})
|
|
.times(2)
|
|
|
|
await client.clientCredentialsGrant(config)
|
|
await client.clientCredentialsGrant(config)
|
|
|
|
t.notThrows(() => agent.assertNoPendingInterceptors())
|
|
})
|
|
|
|
test('DCR can use ClientSecretBasic without having a secret at hand prior to the registration', async (t) => {
|
|
let agent = new undici.MockAgent()
|
|
agent.disableNetConnect()
|
|
|
|
const issuer = new URL('https://op.example.com')
|
|
|
|
const mockAgent = agent.get(issuer.origin)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'GET',
|
|
path: '/.well-known/openid-configuration',
|
|
})
|
|
.reply(200, {
|
|
issuer: issuer.href,
|
|
registration_endpoint: `${issuer.origin}/reg`,
|
|
token_endpoint: `${issuer.origin}/token`,
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
})
|
|
.reply(201, {
|
|
client_id: 'foo',
|
|
client_secret: 'bar',
|
|
client_secret_expires_at: 0,
|
|
})
|
|
|
|
const config = await client.dynamicClientRegistration(
|
|
issuer,
|
|
{},
|
|
client.ClientSecretBasic(),
|
|
{
|
|
// @ts-ignore
|
|
[client.customFetch](url, options) {
|
|
return undici.fetch(url, { ...options, dispatcher: agent })
|
|
},
|
|
},
|
|
)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/token',
|
|
headers(headers) {
|
|
return headers.authorization === 'Basic Zm9vOmJhcg=='
|
|
},
|
|
})
|
|
.reply(200, {
|
|
access_token: 'token',
|
|
token_type: 'bearer',
|
|
})
|
|
.times(2)
|
|
|
|
await client.clientCredentialsGrant(config)
|
|
await client.clientCredentialsGrant(config)
|
|
|
|
t.notThrows(() => agent.assertNoPendingInterceptors())
|
|
})
|
|
|
|
test('DCR can use ClientSecretPost without having a secret at hand prior to the registration', async (t) => {
|
|
let agent = new undici.MockAgent()
|
|
agent.disableNetConnect()
|
|
|
|
const issuer = new URL('https://op.example.com')
|
|
|
|
const mockAgent = agent.get(issuer.origin)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'GET',
|
|
path: '/.well-known/openid-configuration',
|
|
})
|
|
.reply(200, {
|
|
issuer: issuer.href,
|
|
registration_endpoint: `${issuer.origin}/reg`,
|
|
token_endpoint: `${issuer.origin}/token`,
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
})
|
|
.reply(201, {
|
|
client_id: 'foo',
|
|
client_secret: 'bar',
|
|
client_secret_expires_at: 0,
|
|
})
|
|
|
|
const config = await client.dynamicClientRegistration(
|
|
issuer,
|
|
{},
|
|
client.ClientSecretPost(),
|
|
{
|
|
// @ts-ignore
|
|
[client.customFetch](url, options) {
|
|
return undici.fetch(url, { ...options, dispatcher: agent })
|
|
},
|
|
},
|
|
)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/token',
|
|
body(body) {
|
|
const params = new URLSearchParams(body)
|
|
t.true(params.has('client_id'))
|
|
t.true(params.has('client_secret'))
|
|
return true
|
|
},
|
|
})
|
|
.reply(200, {
|
|
access_token: 'token',
|
|
token_type: 'bearer',
|
|
})
|
|
.times(2)
|
|
|
|
await client.clientCredentialsGrant(config)
|
|
await client.clientCredentialsGrant(config)
|
|
|
|
t.notThrows(() => agent.assertNoPendingInterceptors())
|
|
})
|
|
|
|
test('DCR can use ClientSecretJwt without having a secret at hand prior to the registration', async (t) => {
|
|
let agent = new undici.MockAgent()
|
|
agent.disableNetConnect()
|
|
|
|
const issuer = new URL('https://op.example.com')
|
|
|
|
const mockAgent = agent.get(issuer.origin)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'GET',
|
|
path: '/.well-known/openid-configuration',
|
|
})
|
|
.reply(200, {
|
|
issuer: issuer.href,
|
|
registration_endpoint: `${issuer.origin}/reg`,
|
|
token_endpoint: `${issuer.origin}/token`,
|
|
})
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/reg',
|
|
})
|
|
.reply(201, {
|
|
client_id: 'foo',
|
|
client_secret: 'bar',
|
|
client_secret_expires_at: 0,
|
|
})
|
|
|
|
const config = await client.dynamicClientRegistration(
|
|
issuer,
|
|
{},
|
|
client.ClientSecretJwt(),
|
|
{
|
|
// @ts-ignore
|
|
[client.customFetch](url, options) {
|
|
return undici.fetch(url, { ...options, dispatcher: agent })
|
|
},
|
|
},
|
|
)
|
|
|
|
mockAgent
|
|
.intercept({
|
|
method: 'POST',
|
|
path: '/token',
|
|
body(body) {
|
|
const params = new URLSearchParams(body)
|
|
t.true(params.has('client_id'))
|
|
t.true(params.has('client_assertion_type'))
|
|
t.true(params.has('client_assertion'))
|
|
return true
|
|
},
|
|
})
|
|
.reply(200, {
|
|
access_token: 'token',
|
|
token_type: 'bearer',
|
|
})
|
|
.times(2)
|
|
|
|
await client.clientCredentialsGrant(config)
|
|
await client.clientCredentialsGrant(config)
|
|
|
|
t.notThrows(() => agent.assertNoPendingInterceptors())
|
|
})
|