@ -1465,6 +1473,82 @@ async function routes(app: FastifyInstance) {
}
}
)
)
/**
*GETTHEINSTANCE-WIDEAUTHENTICATIONCONFIGURATION
*/
app.get(
'/authentication/config',
{
config:{
permissions:['manage:system']
},
schema:{
summary:'Get the instance-wide authentication settings',
description:
'The settings that hold for every site: whether passkeys may be used, and whether a user may edit their own profile. Which strategies a site offers is part of that site’s configuration instead.',
summary:'Update the instance-wide authentication settings',
description:
'Accepts any subset of the fields, and applies at once on every instance — nothing here is read at boot. Turning passkeys off leaves the registered ones in place: they cannot be used while it is off, and work again as soon as it is back on.',
@ -23,7 +24,7 @@ async function routes(app: FastifyInstance) {
schema:{
schema:{
summary:'Everything the app needs to start',
summary:'Everything the app needs to start',
description:
description:
'The site for the hostname, the system flags, and the current session — the same answers `sites/{hostname}`, `system/flags` and `users/whoami` give, in one request.\n\nCarries the session, so it is never cached.',
'The site for the hostname, the system flags, the instance-wide authentication settings and the current session — the same answers `sites/{hostname}`, `system/flags`, `authentication/config` and `users/whoami` give, in one request.\n\nThe authentication settings are here rather than read from their own endpoint because that one is behind `manage:system`, while what they decide — whether a passkey may be signed in with, whether a profile may be edited — has to be known to whoever is looking, logged in or not.\n\nCarries the session, so it is never cached.',
tags:['System'],
tags:['System'],
querystring:{
querystring:{
type:'object',
type:'object',
@ -37,11 +38,12 @@ async function routes(app: FastifyInstance) {
},
},
response:{
response:{
200:{
200:{
description:'Site, flags and session',
description:'Site, flags, authentication settings and session',
type:'object',
type:'object',
properties:{
properties:{
site:{$ref:'Site#'},
site:{$ref:'Site#'},
flags:{$ref:'SystemFlags#'},
flags:{$ref:'SystemFlags#'},
auth:{$ref:'AuthConfig#'},
user:{
user:{
type:'object',
type:'object',
description:
description:
@ -76,6 +78,7 @@ async function routes(app: FastifyInstance) {
'Whether a passkey may be registered or signed in with. Turned off, the passkeys already registered are kept and start working again the moment it is turned back on.'
},
allowProfileEditing:{
type:'boolean',
description:
'Whether a user may edit their own profile. Off for an instance whose user records are owned by an identity provider.'
@ -273,7 +281,7 @@ async function routes(app: FastifyInstance) {
schema:{
schema:{
summary:"Update the logged in user's own profile",
summary:"Update the logged in user's own profile",
description:
description:
'Updates any subset of the profile fields; omitted ones are left unchanged. Requires the current site to have the `profile` feature enabled. The email cannot be changed here, and neither can any field an administrator owns.',
'Updates any subset of the profile fields; omitted ones are left unchanged. The name, location, job title and pronouns require profile editing to be enabled on this wiki (Administration → Authentication) and are refused otherwise; the time zone, date and time formats, appearance and colour-vision settings are the user’s own and are always accepted. The email cannot be changed here, and neither can any field an administrator owns.',
tags:['Users'],
tags:['Users'],
body:{
body:{
$ref:'UserProfileUpdate#'
$ref:'UserProfileUpdate#'
@ -302,10 +310,6 @@ async function routes(app: FastifyInstance) {
if(!userId){
if(!userId){
returnreply.unauthorized()
returnreply.unauthorized()
}
}
if(!(awaitisProfileEditable(req))){
returnreply.forbidden('Profile editing is disabled on this site.')
}
// -> A bad time zone would break every date the user sees, and the list of valid zones is only
// -> A bad time zone would break every date the user sees, and the list of valid zones is only
// known at runtime, so it cannot be expressed as a schema enum
// known at runtime, so it cannot be expressed as a schema enum
`Profile editing is disabled on this wiki: ${refused.join(', ')} cannot be changed here.`
)
}
}
if(Object.keys(patch).length<1){
if(Object.keys(patch).length<1){
thrownewCustomError('userProfileEmpty','No profile fields provided to update.')
thrownewCustomError('userProfileEmpty','No profile fields provided to update.')
}
}
@ -379,7 +381,7 @@ async function routes(app: FastifyInstance) {
{
{
schema:{
schema:{
summary:"Replace the logged in user's own avatar",
summary:"Replace the logged in user's own avatar",
description:`The body is the raw image, not a multipart form — send the file itself with its \`Content-Type\`. At most ${avatarUploadLimit/1024/1024} MB, and it must really be one of the accepted formats: the bytes are checked, not the declared type. Resized to a 180x180 JPEG when the Sharp extension is installed, otherwise stored as uploaded. Requires the current site to have the \`profile\` feature enabled.`,
description:`The body is the raw image, not a multipart form — send the file itself with its \`Content-Type\`. At most ${avatarUploadLimit/1024/1024} MB, and it must really be one of the accepted formats: the bytes are checked, not the declared type. Resized to a 180x180 JPEG when the Sharp extension is installed, otherwise stored as uploaded. Requires profile editing to be enabled on this wiki (Administration → Authentication).`,
tags:['Users'],
tags:['Users'],
consumes:[...imageMimeTypes],
consumes:[...imageMimeTypes],
response:{
response:{
@ -403,8 +405,8 @@ async function routes(app: FastifyInstance) {
returnreply.forbidden('Profile editing is disabled on this site.')
returnreply.forbidden('Profile editing is disabled on this wiki.')
}
}
constdata=req.body
constdata=req.body
@ -442,7 +444,7 @@ async function routes(app: FastifyInstance) {
schema:{
schema:{
summary:"Remove the logged in user's own avatar",
summary:"Remove the logged in user's own avatar",
description:
description:
'Leaves the user to be rendered as a placeholder again. Succeeds even if there was no avatar to remove. Requires the current site to have the `profile` feature enabled.',
'Leaves the user to be rendered as a placeholder again. Succeeds even if there was no avatar to remove. Requires profile editing to be enabled on this wiki (Administration → Authentication).',
tags:['Users'],
tags:['Users'],
response:{
response:{
200:{
200:{
@ -465,8 +467,8 @@ async function routes(app: FastifyInstance) {
returnreply.forbidden('Profile editing is disabled on this site.')
returnreply.forbidden('Profile editing is disabled on this wiki.')
}
}
awaitWIKI.models.users.clearAvatar(userId)
awaitWIKI.models.users.clearAvatar(userId)
@ -663,6 +665,11 @@ async function routes(app: FastifyInstance) {
type:'boolean',
type:'boolean',
description:
description:
'Whether the account has another way in — a passkey or another linked provider — and may therefore turn password login off.'
'Whether the account has another way in — a passkey or another linked provider — and may therefore turn password login off.'
},
canChangePassword:{
type:'boolean',
description:
'Whether this strategy lets a user change their own password here. False where an administrator has turned `allowPasswordChange` off, which does not stop a password change the wiki itself demands at sign-in.'
}
}
}
}
}
}
@ -700,7 +707,7 @@ async function routes(app: FastifyInstance) {
schema:{
schema:{
summary:"Change the logged in user's own password",
summary:"Change the logged in user's own password",
description:
description:
'The current password has to be given, and is what authorizes the change. Only a provider that stores the password on this instance can be changed here. Also clears any pending forced password change.',
'The current password has to be given, and is what authorizes the change. Only a provider that stores the password on this instance can be changed here, and only while its `allowPasswordChange` setting is on. Also clears any pending forced password change.',
tags:['Users'],
tags:['Users'],
body:{
body:{
type:'object',
type:'object',
@ -733,6 +740,17 @@ async function routes(app: FastifyInstance) {
"admin.auth.addPending":"{strategy} added. It is created when you press Apply.",
"admin.auth.addPending":"{strategy} added. It is created when you press Apply.",
"admin.auth.addStrategy":"Add Strategy",
"admin.auth.addStrategy":"Add Strategy",
"admin.auth.allowPasskeys":"Allow Passkeys",
"admin.auth.allowPasskeysHint":"Can users sign in with a passkey, and register new ones? Passkeys that are already registered are kept while this is off, and work again as soon as you turn it back on.",
"admin.auth.allowProfileEditingHint":"Can users edit their own profile? If profile data is managed by an external identity provider, you should turn this off.",
"admin.auth.allowedEmailRegexHint":"(optional) Only allow users to register with an email address that matches the regex expression.",
"admin.auth.allowedEmailRegexHint":"(optional) Only allow users to register with an email address that matches the regex expression.",
"admin.auth.allowedWebOrigins":"Allowed Web Origins",
"admin.auth.allowedWebOrigins":"Allowed Web Origins",
"admin.auth.autoEnrollGroups":"Assign to group(s)",
"admin.auth.autoEnrollGroups":"Assign to group(s)",
"admin.auth.autoEnrollGroupsHint":"(optional) Automatically assign new users to these groups. New users are always added to the Users group regardless of this setting.",
"admin.auth.autoEnrollGroupsHint":"(optional) Automatically assign new users to these groups. New users are always added to the Users group regardless of this setting.",
"admin.auth.configReferenceSubtitle":"Some strategies may require some configuration values to be set on your provider. These are provided for reference only and may not be needed by the current strategy.",
"admin.auth.configReferenceSubtitle":"Some strategies may require some configuration values to be set on your provider. These are provided for reference only and may not be needed by the current strategy.",
"admin.auth.configSaveFailed":"Failed to save the authentication configuration.",
"admin.auth.deleteConfirm":"Are you sure you want to delete the {strategy} strategy? Users who can only sign in through it will lose access.",
"admin.auth.deleteConfirm":"Are you sure you want to delete the {strategy} strategy? Users who can only sign in through it will lose access.",
"admin.auth.deleteFailed":"Failed to delete the strategy.",
"admin.auth.deleteFailed":"Failed to delete the strategy.",
"admin.auth.deleteLocalForbidden":"Every account is registered against the local strategy, so it cannot be deleted.",
"admin.auth.deleteStrategy":"Delete Strategy",
"admin.auth.deleteStrategy":"Delete Strategy",
"admin.auth.deleteSuccess":"{strategy} has been deleted.",
"admin.auth.deleteSuccess":"{strategy} has been deleted.",
"admin.auth.displayName":"Display Name",
"admin.auth.displayName":"Display Name",
@ -433,8 +440,6 @@
"admin.general.allowCollaborativeEditingHint":"Can several people edit the same page at the same time, seeing each other's cursors and changes live? Applies to the markdown editor. Changes are still only stored when someone saves the page.",
"admin.general.allowCollaborativeEditingHint":"Can several people edit the same page at the same time, seeing each other's cursors and changes live? Applies to the markdown editor. Changes are still only stored when someone saves the page.",
"admin.general.allowComments":"Allow Comments",
"admin.general.allowComments":"Allow Comments",
"admin.general.allowCommentsHint":"Can users leave comments on pages? Can be restricted using Page Rules.",
"admin.general.allowCommentsHint":"Can users leave comments on pages? Can be restricted using Page Rules.",
"admin.general.allowProfileHint":"Can users edit their own profile? If profile data is managed by an external identity provider, you should turn this off.",
"admin.general.allowRatings":"Allow Ratings",
"admin.general.allowRatings":"Allow Ratings",
"admin.general.allowRatingsHint":"Can users leave ratings on pages? Can be restricted using Page Rules.",
"admin.general.allowRatingsHint":"Can users leave ratings on pages? Can be restricted using Page Rules.",
"admin.general.allowSearch":"Allow Search",
"admin.general.allowSearch":"Allow Search",
@ -2477,6 +2482,7 @@
"profile.authInfo":"Your account is associated with the following authentication methods:",
"profile.authInfo":"Your account is associated with the following authentication methods:",
"profile.authLoadingFailed":"Failed to load authentication methods.",
"profile.authLoadingFailed":"Failed to load authentication methods.",
"profile.authModifyTfa":"Modify 2FA",
"profile.authModifyTfa":"Modify 2FA",
"profile.authPasswordChangeDisabled":"Your wiki administrator has disabled password changes for this login method.",
"profile.authPasswordLoginOff":"Password login is turned off for this account.",
"profile.authPasswordLoginOff":"Password login is turned off for this account.",
"profile.authPasswordLoginOnlyMethod":"Register a passkey or link another authentication method before turning this off.",
"profile.authPasswordLoginOnlyMethod":"Register a passkey or link another authentication method before turning this off.",
"profile.authSetTfa":"Set 2FA",
"profile.authSetTfa":"Set 2FA",
@ -2504,7 +2510,7 @@
"profile.dateFormatHint":"Set your preferred format to display dates.",
"profile.dateFormatHint":"Set your preferred format to display dates.",
"profile.displayName":"Display Name",
"profile.displayName":"Display Name",
"profile.displayNameHint":"Your full name; shown when authoring content (e.g. pages, comments, etc.).",
"profile.displayNameHint":"Your full name; shown when authoring content (e.g. pages, comments, etc.).",
"profile.editDisabledDescription":"Your wiki administrator has disabled profile editing.",
"profile.editDisabledDescription":"Your wiki administrator has disabled profile editing. Your preferences and accessibility settings below can still be changed.",
"profile.editDisabledTitle":"Profile info is managed by your organization.",
"profile.editDisabledTitle":"Profile info is managed by your organization.",
"profile.email":"Email Address",
"profile.email":"Email Address",
"profile.emailHint":"The email address used for login.",
"profile.emailHint":"The email address used for login.",
@ -2533,6 +2539,7 @@
"profile.passkeysDeactivateConfirm":"Are you sure you want to deactivate this passkey?",
"profile.passkeysDeactivateConfirm":"Are you sure you want to deactivate this passkey?",
"profile.passkeysDeactivateFailed":"Failed to deactivate the passkey.",
"profile.passkeysDeactivateFailed":"Failed to deactivate the passkey.",
"profile.passkeysDeactivateSuccess":"Passkey deactivated successfully. You may still need to remove the passkey from your device.",
"profile.passkeysDeactivateSuccess":"Passkey deactivated successfully. You may still need to remove the passkey from your device.",
"profile.passkeysDisabled":"Passkeys are turned off on this wiki. Any you have registered are kept, and work again if an administrator turns them back on.",
"profile.passkeysIntro":"Passkeys are a replacement for passwords for a faster, easier and more secure login. It relies on your device existing biometrics (phone, computer, security key) to validate your identity.",
"profile.passkeysIntro":"Passkeys are a replacement for passwords for a faster, easier and more secure login. It relies on your device existing biometrics (phone, computer, security key) to validate your identity.",
"profile.passkeysInvalidName":"Passkey name is missing or invalid.",
"profile.passkeysInvalidName":"Passkey name is missing or invalid.",
hint:Send a verification email with a validation link when somebody registers, and refuse them a login until they follow it. Requires a configured mail server — registration is refused outright without one.
hint:Send a verification email with a validation link when somebody registers, and refuse them a login until they follow it. Requires a configured mail server — registration is refused outright without one.
icon:received
icon:received
default:true
default:true
allowPasswordChange:
type:Boolean
title:Allow Password Change
hint:Users can change their own password from their profile page. Turn this off where passwords are set by an administrator, or where the account's password is managed somewhere else.