fix(oidc): migrate OIDC endpoints and internal handlers to use typedserver IRequestContext and update dependencies

This commit is contained in:
2025-12-22 15:56:20 +00:00
parent a91dd9dda6
commit 32ffc1bbaa
15 changed files with 883 additions and 756 deletions
+9
View File
@@ -1,5 +1,14 @@
# Changelog # Changelog
## 2025-12-22 - 1.14.1 - fix(oidc)
migrate OIDC endpoints and internal handlers to use typedserver IRequestContext and update dependencies
- Updated route handlers in ts/index.ts to pass ctx (IRequestContext) instead of req
- Refactored OIDC manager handlers to accept plugins.typedserver.IRequestContext and use ctx.url, ctx.headers, ctx.formData (handleAuthorize, handleToken, handleUserInfo, handleRevoke)
- Bumped dependencies to support the new typedserver API: @api.global/typedserver -> ^8.1.0
- Other dependency updates: @design.estate/dees-catalog ^3.4.0, @git.zone/tspublish ^1.11.0, @types/node ^25.0.3
- Changing public handler method signatures is a breaking API change; recommend a major version bump
## 2025-12-16 - 1.14.0 - feat(docs) ## 2025-12-16 - 1.14.0 - feat(docs)
add package READMEs and publish metadata; update web package publish order add package READMEs and publish metadata; update web package publish order
+4 -4
View File
@@ -18,13 +18,13 @@
"dependencies": { "dependencies": {
"@api.global/typedrequest": "^3.2.5", "@api.global/typedrequest": "^3.2.5",
"@api.global/typedrequest-interfaces": "^3.0.19", "@api.global/typedrequest-interfaces": "^3.0.19",
"@api.global/typedserver": "^7.11.1", "@api.global/typedserver": "^8.1.0",
"@api.global/typedsocket": "^4.1.0", "@api.global/typedsocket": "^4.1.0",
"@consent.software/catalog": "^2.0.1", "@consent.software/catalog": "^2.0.1",
"@design.estate/dees-catalog": "^3.3.1", "@design.estate/dees-catalog": "^3.4.0",
"@design.estate/dees-domtools": "^2.3.6", "@design.estate/dees-domtools": "^2.3.6",
"@design.estate/dees-element": "^2.1.3", "@design.estate/dees-element": "^2.1.3",
"@git.zone/tspublish": "^1.10.3", "@git.zone/tspublish": "^1.11.0",
"@push.rocks/lik": "^6.2.2", "@push.rocks/lik": "^6.2.2",
"@push.rocks/qenv": "^6.1.3", "@push.rocks/qenv": "^6.1.3",
"@push.rocks/smartcli": "^4.0.19", "@push.rocks/smartcli": "^4.0.19",
@@ -58,7 +58,7 @@
"@git.zone/tsrun": "^2.0.1", "@git.zone/tsrun": "^2.0.1",
"@git.zone/tswatch": "^2.3.13", "@git.zone/tswatch": "^2.3.13",
"@push.rocks/projectinfo": "^5.0.1", "@push.rocks/projectinfo": "^5.0.1",
"@types/node": "^24.10.1" "@types/node": "^25.0.3"
}, },
"private": true, "private": true,
"repository": { "repository": {
+804 -674
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@idp.global/idp.global', name: '@idp.global/idp.global',
version: '1.14.0', version: '1.14.1',
description: 'An identity provider software managing user authentications, registrations, and sessions.' description: 'An identity provider software managing user authentications, registrations, and sessions.'
} }
+12 -12
View File
@@ -28,40 +28,40 @@ export const runCli = async () => {
typedserver.options.spaFallback = true; typedserver.options.spaFallback = true;
// OIDC Discovery endpoint // OIDC Discovery endpoint
typedserver.addRoute('/.well-known/openid-configuration', 'GET', async (req) => { typedserver.addRoute('/.well-known/openid-configuration', 'GET', async (ctx) => {
return new Response(JSON.stringify(reception.oidcManager.getDiscoveryDocument()), { return new Response(JSON.stringify(reception.oidcManager.getDiscoveryDocument()), {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
}); });
}); });
// JWKS endpoint // JWKS endpoint
typedserver.addRoute('/.well-known/jwks.json', 'GET', async (req) => { typedserver.addRoute('/.well-known/jwks.json', 'GET', async (ctx) => {
return new Response(JSON.stringify(reception.oidcManager.getJwks()), { return new Response(JSON.stringify(reception.oidcManager.getJwks()), {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
}); });
}); });
// OAuth Authorization endpoint // OAuth Authorization endpoint
typedserver.addRoute('/oauth/authorize', 'GET', async (req) => { typedserver.addRoute('/oauth/authorize', 'GET', async (ctx) => {
return reception.oidcManager.handleAuthorize(req); return reception.oidcManager.handleAuthorize(ctx);
}); });
// OAuth Token endpoint // OAuth Token endpoint
typedserver.addRoute('/oauth/token', 'POST', async (req) => { typedserver.addRoute('/oauth/token', 'POST', async (ctx) => {
return reception.oidcManager.handleToken(req); return reception.oidcManager.handleToken(ctx);
}); });
// OAuth UserInfo endpoint (GET and POST) // OAuth UserInfo endpoint (GET and POST)
typedserver.addRoute('/oauth/userinfo', 'GET', async (req) => { typedserver.addRoute('/oauth/userinfo', 'GET', async (ctx) => {
return reception.oidcManager.handleUserInfo(req); return reception.oidcManager.handleUserInfo(ctx);
}); });
typedserver.addRoute('/oauth/userinfo', 'POST', async (req) => { typedserver.addRoute('/oauth/userinfo', 'POST', async (ctx) => {
return reception.oidcManager.handleUserInfo(req); return reception.oidcManager.handleUserInfo(ctx);
}); });
// OAuth Revocation endpoint // OAuth Revocation endpoint
typedserver.addRoute('/oauth/revoke', 'POST', async (req) => { typedserver.addRoute('/oauth/revoke', 'POST', async (ctx) => {
return reception.oidcManager.handleRevoke(req); return reception.oidcManager.handleRevoke(ctx);
}); });
}, },
}); });
+10 -11
View File
@@ -95,9 +95,8 @@ export class OidcManager {
/** /**
* Handle the authorization endpoint request * Handle the authorization endpoint request
*/ */
public async handleAuthorize(request: Request): Promise<Response> { public async handleAuthorize(ctx: plugins.typedserver.IRequestContext): Promise<Response> {
const url = new URL(request.url); const params = ctx.url.searchParams;
const params = url.searchParams;
// Extract authorization request parameters // Extract authorization request parameters
const clientId = params.get('client_id'); const clientId = params.get('client_id');
@@ -196,21 +195,21 @@ export class OidcManager {
/** /**
* Handle the token endpoint request * Handle the token endpoint request
*/ */
public async handleToken(request: Request): Promise<Response> { public async handleToken(ctx: plugins.typedserver.IRequestContext): Promise<Response> {
// Parse form data // Parse form data
const contentType = request.headers.get('content-type'); const contentType = ctx.headers.get('content-type');
if (!contentType?.includes('application/x-www-form-urlencoded')) { if (!contentType?.includes('application/x-www-form-urlencoded')) {
return this.tokenErrorResponse('invalid_request', 'Content-Type must be application/x-www-form-urlencoded'); return this.tokenErrorResponse('invalid_request', 'Content-Type must be application/x-www-form-urlencoded');
} }
const formData = await request.formData(); const formData = await ctx.formData();
const grantType = formData.get('grant_type') as string; const grantType = formData.get('grant_type') as string;
// Extract client credentials from Basic auth or form // Extract client credentials from Basic auth or form
let clientId = formData.get('client_id') as string; let clientId = formData.get('client_id') as string;
let clientSecret = formData.get('client_secret') as string; let clientSecret = formData.get('client_secret') as string;
const authHeader = request.headers.get('authorization'); const authHeader = ctx.headers.get('authorization');
if (authHeader?.startsWith('Basic ')) { if (authHeader?.startsWith('Basic ')) {
const base64 = authHeader.substring(6); const base64 = authHeader.substring(6);
const decoded = Buffer.from(base64, 'base64').toString('utf-8'); const decoded = Buffer.from(base64, 'base64').toString('utf-8');
@@ -469,9 +468,9 @@ export class OidcManager {
/** /**
* Handle the userinfo endpoint * Handle the userinfo endpoint
*/ */
public async handleUserInfo(request: Request): Promise<Response> { public async handleUserInfo(ctx: plugins.typedserver.IRequestContext): Promise<Response> {
// Get access token from Authorization header // Get access token from Authorization header
const authHeader = request.headers.get('authorization'); const authHeader = ctx.headers.get('authorization');
if (!authHeader?.startsWith('Bearer ')) { if (!authHeader?.startsWith('Bearer ')) {
return new Response(JSON.stringify({ error: 'invalid_token' }), { return new Response(JSON.stringify({ error: 'invalid_token' }), {
status: 401, status: 401,
@@ -575,8 +574,8 @@ export class OidcManager {
/** /**
* Handle the revocation endpoint * Handle the revocation endpoint
*/ */
public async handleRevoke(request: Request): Promise<Response> { public async handleRevoke(ctx: plugins.typedserver.IRequestContext): Promise<Response> {
const formData = await request.formData(); const formData = await ctx.formData();
const token = formData.get('token') as string; const token = formData.get('token') as string;
const tokenTypeHint = formData.get('token_type_hint') as string; const tokenTypeHint = formData.get('token_type_hint') as string;
+1 -1
View File
@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@idp.global/idp.global', name: '@idp.global/idp.global',
version: '1.14.0', version: '1.14.1',
description: 'An identity provider software managing user authentications, registrations, and sessions.' description: 'An identity provider software managing user authentications, registrations, and sessions.'
} }
+13 -7
View File
@@ -30,6 +30,19 @@ export const cardStyles = css`
} }
`; `;
/**
* Base styles for all view components
* Provides consistent background and foreground colors
*/
export const viewBaseStyles = css`
:host {
display: block;
min-height: 100%;
background: var(--background);
color: var(--foreground);
}
`;
/** /**
* Typography styles for consistent text hierarchy * Typography styles for consistent text hierarchy
*/ */
@@ -108,10 +121,3 @@ export const navigationStyles = css`
} }
`; `;
/**
* Legacy export for backwards compatibility
*/
export default css`
${accountDesignTokens}
${typographyStyles}
`;
+3 -9
View File
@@ -11,7 +11,7 @@ import {
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import { IdpState } from '../../../states/idp.state.js'; import { IdpState } from '../../../states/idp.state.js';
import { accountDesignTokens } from '../sharedstyles.js'; import * as sharedStyles from '../sharedstyles.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
@@ -43,15 +43,9 @@ export class AdminView extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
accountDesignTokens, sharedStyles.accountDesignTokens,
sharedStyles.viewBaseStyles,
css` css`
:host {
display: block;
min-height: 100%;
background: var(--background);
color: var(--foreground);
}
.container { .container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
+5 -5
View File
@@ -9,7 +9,7 @@ import {
state, state,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import sharedStyles, { accountDesignTokens, cardStyles, typographyStyles } from '../sharedstyles.js'; import * as sharedStyles from '../sharedstyles.js';
import * as accountState from '../../../states/accountstate.js'; import * as accountState from '../../../states/accountstate.js';
import { IdpState } from '../../../states/idp.state.js'; import { IdpState } from '../../../states/idp.state.js';
@@ -45,12 +45,12 @@ export class AppsView extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
accountDesignTokens, sharedStyles.accountDesignTokens,
cardStyles, sharedStyles.viewBaseStyles,
typographyStyles, sharedStyles.cardStyles,
sharedStyles.typographyStyles,
css` css`
:host { :host {
display: block;
padding: 48px; padding: 48px;
max-width: 1000px; max-width: 1000px;
margin: 0 auto; margin: 0 auto;
+3 -9
View File
@@ -10,7 +10,7 @@ import {
type TemplateResult, type TemplateResult,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import { accountDesignTokens } from '../sharedstyles.js'; import * as sharedStyles from '../sharedstyles.js';
import * as accountStateModule from '../../../states/accountstate.js'; import * as accountStateModule from '../../../states/accountstate.js';
import { IdpState } from '../../../states/idp.state.js'; import { IdpState } from '../../../states/idp.state.js';
@@ -59,15 +59,9 @@ export class BaseView extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
accountDesignTokens, sharedStyles.accountDesignTokens,
sharedStyles.viewBaseStyles,
css` css`
:host {
display: block;
min-height: 100%;
background: var(--background);
color: var(--foreground);
}
.container { .container {
max-width: 1000px; max-width: 1000px;
margin: 0 auto; margin: 0 auto;
+3 -8
View File
@@ -10,7 +10,7 @@ import {
type TemplateResult, type TemplateResult,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import { accountDesignTokens } from '../sharedstyles.js'; import * as sharedStyles from '../sharedstyles.js';
import * as accountStateModule from '../../../states/accountstate.js'; import * as accountStateModule from '../../../states/accountstate.js';
import { IdpState } from '../../../states/idp.state.js'; import { IdpState } from '../../../states/idp.state.js';
@@ -41,14 +41,9 @@ export class OrgView extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
accountDesignTokens, sharedStyles.accountDesignTokens,
sharedStyles.viewBaseStyles,
css` css`
:host {
display: block;
min-height: 100%;
background: var(--background);
color: var(--foreground);
}
.container { .container {
max-width: 1000px; max-width: 1000px;
+5 -5
View File
@@ -9,7 +9,7 @@ import {
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import * as plugins from '../../../plugins.js'; import * as plugins from '../../../plugins.js';
import sharedStyles from '../sharedstyles.js'; import * as sharedStyles from '../sharedstyles.js';
import * as state from '../../../states/accountstate.js'; import * as state from '../../../states/accountstate.js';
import { IdpState } from '../../../states/idp.state.js'; import { IdpState } from '../../../states/idp.state.js';
@@ -23,13 +23,13 @@ declare global {
export class PaddleSetupView extends DeesElement { export class PaddleSetupView extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
sharedStyles, sharedStyles.accountDesignTokens,
sharedStyles.viewBaseStyles,
css` css`
:host { :host {
display: block; padding: 48px;
max-width: 900px; max-width: 900px;
margin: auto; margin: 0 auto;
color: ${cssManager.bdTheme('#333', '#fff')};
} }
`, `,
]; ];
@@ -8,7 +8,7 @@ import {
css, css,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import sharedStyles, { accountDesignTokens, cardStyles, typographyStyles } from '../sharedstyles.js'; import * as sharedStyles from '../sharedstyles.js';
import * as state from '../../../states/accountstate.js'; import * as state from '../../../states/accountstate.js';
@@ -46,12 +46,12 @@ export class SubscriptionView extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
accountDesignTokens, sharedStyles.accountDesignTokens,
cardStyles, sharedStyles.viewBaseStyles,
typographyStyles, sharedStyles.cardStyles,
sharedStyles.typographyStyles,
css` css`
:host { :host {
display: block;
padding: 48px; padding: 48px;
max-width: 900px; max-width: 900px;
margin: 0 auto; margin: 0 auto;
+5 -5
View File
@@ -9,7 +9,7 @@ import {
type TemplateResult, type TemplateResult,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import sharedStyles, { accountDesignTokens, cardStyles, typographyStyles } from '../sharedstyles.js'; import * as sharedStyles from '../sharedstyles.js';
import * as accountState from '../../../states/accountstate.js'; import * as accountState from '../../../states/accountstate.js';
import { IdpState } from '../../../states/idp.state.js'; import { IdpState } from '../../../states/idp.state.js';
import { BulkInviteModal } from '../bulk-invite-modal.js'; import { BulkInviteModal } from '../bulk-invite-modal.js';
@@ -83,12 +83,12 @@ export class UsersView extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
accountDesignTokens, sharedStyles.accountDesignTokens,
cardStyles, sharedStyles.viewBaseStyles,
typographyStyles, sharedStyles.cardStyles,
sharedStyles.typographyStyles,
css` css`
:host { :host {
display: block;
padding: 48px; padding: 48px;
max-width: 1000px; max-width: 1000px;
margin: 0 auto; margin: 0 auto;