feat(center): OIDC login (auto-provision by email) + CLI config-oidc
- OidcConfig entity (single row) + DB registration.
- OidcService: discovery, Authorization Code + PKCE, state/ticket as
short-lived signed JWTs (no server session), userinfo/id_token claim
resolution. Auto-provisions a passwordless user by email.
- Public endpoints /auth/oidc/{status,start,callback,exchange}; callback
bounces a one-time ticket to the SPA in the URL fragment.
- AuthService.provisionOidcUser + issueSessionForUserId (login shape).
- CLI: 'config oidc' upserts issuer/clientId/secret/callback/redirect/
scopes/enabled (secret masked on print).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
42
src/cli.ts
42
src/cli.ts
@@ -2,6 +2,7 @@ import 'reflect-metadata';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module.js';
|
||||
import { AuthService } from './auth/auth.service.js';
|
||||
import { OidcService } from './auth/oidc.service.js';
|
||||
import { NodeAdminService } from './nodes/node-admin.service.js';
|
||||
|
||||
function getArg(flag: string): string | null {
|
||||
@@ -15,6 +16,11 @@ function printUsageAndExit(): never {
|
||||
console.error(' node dist/cli.js user create --email <email> --password <password>');
|
||||
console.error(' node dist/cli.js user apikey --email <email> [--label <label>]');
|
||||
console.error(' node dist/cli.js node register --node-id <id> --name <name> --endpoint <url>');
|
||||
console.error(
|
||||
' node dist/cli.js config oidc [--issuer <url>] [--client-id <id>] [--client-secret <s>]\n' +
|
||||
' [--callback-url <url>] [--post-login-redirect <url>] [--scopes "openid email profile"]\n' +
|
||||
' [--enabled true|false] (sets provided fields; prints config with secret masked)',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -58,6 +64,42 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (subject === 'config' && action === 'oidc') {
|
||||
const oidc = app.get(OidcService);
|
||||
const patch: Record<string, unknown> = {};
|
||||
const issuer = getArg('--issuer');
|
||||
if (issuer !== null) patch.issuer = issuer;
|
||||
const clientId = getArg('--client-id');
|
||||
if (clientId !== null) patch.clientId = clientId;
|
||||
const clientSecret = getArg('--client-secret');
|
||||
if (clientSecret !== null) patch.clientSecret = clientSecret;
|
||||
const callbackUrl = getArg('--callback-url');
|
||||
if (callbackUrl !== null) patch.redirectUri = callbackUrl;
|
||||
const postLogin = getArg('--post-login-redirect');
|
||||
if (postLogin !== null) patch.postLoginRedirect = postLogin;
|
||||
const scopes = getArg('--scopes');
|
||||
if (scopes !== null) patch.scopes = scopes;
|
||||
const enabled = getArg('--enabled');
|
||||
if (enabled !== null) patch.enabled = enabled === 'true';
|
||||
|
||||
const saved = await oidc.setConfig(patch);
|
||||
process.stdout.write(
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
config: {
|
||||
issuer: saved.issuer,
|
||||
clientId: saved.clientId,
|
||||
clientSecret: saved.clientSecret ? '***set***' : '',
|
||||
redirectUri: saved.redirectUri,
|
||||
postLoginRedirect: saved.postLoginRedirect,
|
||||
scopes: saved.scopes,
|
||||
enabled: saved.enabled,
|
||||
},
|
||||
}) + '\n',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
printUsageAndExit();
|
||||
} finally {
|
||||
await app.close();
|
||||
|
||||
Reference in New Issue
Block a user