Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@hellocoop/mockin",
"private": false,
"version": "2.0.0",
"version": "2.0.1",
"description": "Hellō Mock Login OpenID Connect Server",
"engines": {
"node": "~22"
Expand Down
16 changes: 11 additions & 5 deletions src/aauth/verify-resource-token.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,22 @@ export async function verifyResourceToken(

// ── Step 6 ─────────────────────────────────────────────────────────
// The claims a resource copies out of the person token it verified.
if (!payload.person_token_jti) {
return { error: 'resource_token missing person_token_jti' }
// Spec issue #95 renamed `person_token_jti` → `presented_jti` (same
// value); resources dual-emit during the transition, so accept either,
// preferring the canonical name.
const presentedJti = payload.presented_jti ?? payload.person_token_jti
if (!presentedJti) {
return {
error: 'resource_token missing presented_jti (person_token_jti)',
}
}
if (!payload.ps) return { error: 'resource_token missing ps' }
if (!payload.sub) return { error: 'resource_token missing sub' }

const issued = getPersonToken(payload.person_token_jti)
const issued = getPersonToken(presentedJti)
if (!issued) {
return {
error: `person_token_jti "${payload.person_token_jti}" names no person token this PS issued (or it has expired)`,
error: `presented_jti "${presentedJti}" names no person token this PS issued (or it has expired)`,
}
}
if (payload.ps !== issued.ps) {
Expand Down Expand Up @@ -143,7 +149,7 @@ export async function verifyResourceToken(
scope: typeof payload.scope === 'string' ? payload.scope : '',
ps: payload.ps,
sub: payload.sub,
person_token_jti: payload.person_token_jti,
person_token_jti: presentedJti,
mission_s256: payload.mission_s256 || null,
tenant: payload.tenant || null,
account: payload.account || null,
Expand Down
9 changes: 9 additions & 0 deletions test/aauth/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ export async function mintResourceToken({
r3_s256 = null,
ttl = 300,
personToken = null,
// Which name(s) carry the person-token jti (spec issue #95 rename):
// 'legacy' = person_token_jti only (pre-rename resources),
// 'presented' = presented_jti only (post-transition resources),
// 'both' = dual-emit (transition resources, @aauth/resource 2.1.0).
jti_claim = 'legacy',
} = {}) {
// Given a person token, copy from it — the normal case. Pass `false`
// for any field to omit it deliberately (what a resource stripping a
Expand Down Expand Up @@ -166,6 +171,10 @@ export async function mintResourceToken({
for (const k of ['ps', 'sub', 'person_token_jti']) {
if (!payload[k]) delete payload[k]
}
if (payload.person_token_jti && jti_claim !== 'legacy') {
payload.presented_jti = payload.person_token_jti
if (jti_claim === 'presented') delete payload.person_token_jti
}
if (mission_s256) payload.mission_s256 = mission_s256
if (tenant) payload.tenant = tenant
if (account) payload.account = account
Expand Down
20 changes: 20 additions & 0 deletions test/aauth/token.errors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ describe('AAuth auth_token_endpoint — errors', function () {
expect(response.json().detail).to.match(/person_token_jti/)
})

it('accepts the renamed presented_jti claim alone (spec issue #95)', async function () {
const { person_token } = await getPersonToken(fastify)
const resourceToken = await mintResourceToken({
personToken: person_token,
jti_claim: 'presented',
})
const response = await postResourceToken(resourceToken)
expect(response.statusCode).to.equal(200)
})

it('accepts a dual-emit token carrying both jti claim names', async function () {
const { person_token } = await getPersonToken(fastify)
const resourceToken = await mintResourceToken({
personToken: person_token,
jti_claim: 'both',
})
const response = await postResourceToken(resourceToken)
expect(response.statusCode).to.equal(200)
})

it('rejects a person_token_jti this PS never issued', async function () {
const { person_token } = await getPersonToken(fastify)
const resourceToken = await mintResourceToken({
Expand Down