Steam is a built-in provider beside Google and Discord (STEAM_SIGNIN.md, D392-D396). OpenID 2.0: the assertion is verified server to server with check_authentication, return_to must match byte for byte, and the claimed id must be a 17-digit SteamID64. A Steam Web API key is required to enable it (D394); persona name and avatar are stored on the identity and refreshed at each sign-in, and a failed lookup never blocks one. Steam never signs anybody up (D395), on the web or the mobile bridge. Several Steam accounts may be linked; DELETE /auth/me/account/identities/:provider takes ?id= to unlink one. MODULE_API 1.17.0: ctx.identities.list, api.onIdentityLinking (refuse-only, fails closed, module text never shown) and api.onIdentityLinked. No unlink hook (D393). Schema additive; OpenAPI, routes and engagement manifests regenerated with no modules installed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
104 lines
7.1 KiB
JavaScript
104 lines
7.1 KiB
JavaScript
// Admin · Auth Providers — SSO/OAuth2/OIDC provider configuration.
|
|
//
|
|
// Mounted at /api/v1/admin/auth by admin/index.js, which already applied
|
|
// `noindex, isLoggedIn, staffOnly`; the routes below are /providers under that,
|
|
// so the emitted URLs stay /api/v1/admin/auth/providers[/:id].
|
|
//
|
|
// Admin-only: these rows carry client secrets (write-only, AES-GCM at rest via
|
|
// utils/secretBox.js) and decide which external identities may sign in at all.
|
|
|
|
const express = require('express')
|
|
const { body, param } = require('express-validator')
|
|
|
|
const authProviders = require('./authProviders.controller')
|
|
const { requireRole } = require('../../../utils/auth')
|
|
const validate = require('../../../middleware/validate')
|
|
|
|
const providersRouter = express.Router()
|
|
const adminOnly = requireRole('admin')
|
|
|
|
providersRouter.get(
|
|
'/providers',
|
|
// #swagger.tags = ['Admin · Auth Providers']
|
|
// #swagger.summary = 'List configured SSO providers (admin only)'
|
|
// #swagger.security = [{ "cookieAuth": [] }, { "bearerAuth": [] }]
|
|
/* #swagger.responses[200] = { description: 'Providers (secrets stripped)', content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/ProviderConfig" } } } } } */
|
|
/* #swagger.responses[401] = { description: 'Not authenticated', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[403] = { description: 'Admin role required', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
adminOnly,
|
|
authProviders.list,
|
|
)
|
|
providersRouter.post(
|
|
'/providers',
|
|
// #swagger.tags = ['Admin · Auth Providers']
|
|
// #swagger.summary = 'Create a custom SSO provider (admin only)'
|
|
// #swagger.description = 'Built-in providers (google, discord) are configured via PUT, not created here.'
|
|
// #swagger.security = [{ "cookieAuth": [] }, { "bearerAuth": [] }]
|
|
/* #swagger.requestBody = { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/ProviderCreateRequest" } } } } */
|
|
/* #swagger.responses[201] = { description: 'Created provider', content: { "application/json": { schema: { $ref: "#/components/schemas/ProviderConfig" } } } } */
|
|
/* #swagger.responses[400] = { description: 'Validation error, or a built-in/invalid kind', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[401] = { description: 'Not authenticated', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[403] = { description: 'Admin role required', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[409] = { description: 'Provider id already exists', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
adminOnly,
|
|
body('id').matches(/^[a-z0-9-]+$/),
|
|
body('kind').isIn(['oidc', 'oauth2']),
|
|
body('name').isString().trim().notEmpty().isLength({ max: 80 }),
|
|
body('enabled').optional().isBoolean(),
|
|
body('clientId').optional({ values: 'falsy' }).isString(),
|
|
body('secret').optional({ values: 'falsy' }).isString(),
|
|
body('authorizeUrl').optional({ values: 'falsy' }).isURL({ require_tld: false }),
|
|
body('tokenUrl').optional({ values: 'falsy' }).isURL({ require_tld: false }),
|
|
body('userinfoUrl').optional({ values: 'falsy' }).isURL({ require_tld: false }),
|
|
body('scopes').optional({ values: 'falsy' }).isString().isLength({ max: 500 }),
|
|
body('priority').optional().isInt(),
|
|
validate,
|
|
authProviders.create,
|
|
)
|
|
providersRouter.put(
|
|
'/providers/:id',
|
|
// #swagger.tags = ['Admin · Auth Providers']
|
|
// #swagger.summary = 'Update an SSO provider (admin only)'
|
|
// #swagger.description = 'Built-ins (google, discord, steam) take only enabled + credentials. Steam has no client id: its secret is a Steam Web API key, and enabling Steam without one stored or given is refused with 400 (STEAM_SIGNIN.md D394).'
|
|
// #swagger.security = [{ "cookieAuth": [] }, { "bearerAuth": [] }]
|
|
// #swagger.parameters['id'] = { in: 'path', required: true, schema: { type: 'string' }, description: 'Provider id.' }
|
|
/* #swagger.requestBody = { content: { "application/json": { schema: { $ref: "#/components/schemas/ProviderCreateRequest" } } } } */
|
|
/* #swagger.responses[200] = { description: 'Updated provider', content: { "application/json": { schema: { $ref: "#/components/schemas/ProviderConfig" } } } } */
|
|
/* #swagger.responses[400] = { description: 'Validation error, or Steam enabled without a Steam Web API key', content: { "application/json": { schema: { $ref: "#/components/schemas/ValidationError" } } } } */
|
|
/* #swagger.responses[401] = { description: 'Not authenticated', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[403] = { description: 'Admin role required', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[404] = { description: 'Provider not found', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
adminOnly,
|
|
param('id').matches(/^[a-z0-9-]+$/),
|
|
body('name').optional().isString().trim().notEmpty().isLength({ max: 80 }),
|
|
body('enabled').optional().isBoolean(),
|
|
body('clientId').optional({ values: 'falsy' }).isString(),
|
|
body('secret').optional({ values: 'falsy' }).isString(),
|
|
body('authorizeUrl').optional({ values: 'falsy' }).isURL({ require_tld: false }),
|
|
body('tokenUrl').optional({ values: 'falsy' }).isURL({ require_tld: false }),
|
|
body('userinfoUrl').optional({ values: 'falsy' }).isURL({ require_tld: false }),
|
|
body('scopes').optional({ values: 'falsy' }).isString().isLength({ max: 500 }),
|
|
body('priority').optional().isInt(),
|
|
validate,
|
|
authProviders.update,
|
|
)
|
|
providersRouter.delete(
|
|
'/providers/:id',
|
|
// #swagger.tags = ['Admin · Auth Providers']
|
|
// #swagger.summary = 'Delete a custom SSO provider (admin only)'
|
|
// #swagger.description = 'Built-in providers cannot be deleted — disable them instead.'
|
|
// #swagger.security = [{ "cookieAuth": [] }, { "bearerAuth": [] }]
|
|
// #swagger.parameters['id'] = { in: 'path', required: true, schema: { type: 'string' }, description: 'Provider id.' }
|
|
/* #swagger.responses[200] = { description: 'Deleted', content: { "application/json": { schema: { $ref: "#/components/schemas/DeletedFlag" } } } } */
|
|
/* #swagger.responses[400] = { description: 'Built-in provider cannot be deleted', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[401] = { description: 'Not authenticated', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[403] = { description: 'Admin role required', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
/* #swagger.responses[404] = { description: 'Provider not found', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
|
|
adminOnly,
|
|
param('id').matches(/^[a-z0-9-]+$/),
|
|
validate,
|
|
authProviders.remove,
|
|
)
|
|
|
|
module.exports = providersRouter
|