Enable SSO signups (#152)

* feat: enableSingleSignOnSignUp support

* chore: simplify return statement

* chore: linted

* feat: verbose error on not enabled SSO sign up

* chore: update config key to match others

* chore: add key to config sample

* chore: apply suggestions from code review

Co-authored-by: BrunoBernardino <me@brunobernardino.com>

---------

Co-authored-by: BrunoBernardino <me@brunobernardino.com>
This commit is contained in:
Piotr Łoboda 2026-02-26 14:31:19 +01:00 committed by GitHub
parent 917649b97a
commit 0d20b4a337
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 12 additions and 9 deletions

View file

@ -11,6 +11,7 @@ const config: PartialDeep<Config> = {
// allowedCookieDomains: ['example.com', 'example.net'], // Can be set to allow more than the baseUrl's domain for session cookies
// skipCookieDomainSecurity: true, // If true, the cookie domain will not be strictly set and checked against. This skipping slightly reduces security, but is usually necessary for reverse proxies like Cloudflare Tunnel
// enableSingleSignOn: false, // If true, single sign-on will be enabled
// allowSignupsViaSingleSignOn: false, // If true, signups via single sign-on will be allowed, overriding allowSignups
// singleSignOnUrl: '', // The Discovery URL (AKA Issuer) of the identity/single sign-on provider
// singleSignOnEmailAttribute: 'email', // The attribute to prefer as email of the identity/single sign-on provider
// singleSignOnScopes: ['openid', 'email'], // The scopes to request from the identity/single sign-on provider

View file

@ -16,6 +16,7 @@ export class AppConfig {
allowedCookieDomains: [],
skipCookieDomainSecurity: false,
enableSingleSignOn: false,
allowSignupsViaSingleSignOn: false,
singleSignOnUrl: '',
singleSignOnEmailAttribute: 'email',
singleSignOnScopes: ['openid', 'email'],
@ -117,18 +118,13 @@ export class AppConfig {
return this.config;
}
static async isSignupAllowed(): Promise<boolean> {
static async isSignupAllowed({ viaSingleSignOn = false }: { viaSingleSignOn?: boolean } = {}): Promise<boolean> {
await this.loadConfig();
const areSignupsAllowed = this.config.auth.allowSignups;
const areSignupsAllowed = viaSingleSignOn && !this.config.auth.allowSignups ? this.config.auth.allowSingleSignOnSignups : this.config.auth.allowSignups;
const areThereAdmins = await UserModel.isThereAnAdmin();
if (areSignupsAllowed || !areThereAdmins) {
return true;
}
return false;
return areSignupsAllowed || !areThereAdmins;
}
static async isAppEnabled(app: OptionalApp): Promise<boolean> {

View file

@ -169,7 +169,7 @@ export class OidcModel {
throw new Error(`Missing user/${emailAttribute}`);
}
const isSignupAllowed = await AppConfig.isSignupAllowed();
const isSignupAllowed = await AppConfig.isSignupAllowed({ viaSingleSignOn: true });
const isThereAnAdmin = await UserModel.isThereAnAdmin();
// Confirm the user exists (or signup if allowed)
@ -181,6 +181,10 @@ export class OidcModel {
}
if (!user) {
if (!config.auth.allowSignupsViaSingleSignOn) {
throw new Error('Sign up via SSO is not allowed!');
}
throw new Error('There was a problem signing up or logging in!');
}

View file

@ -159,6 +159,8 @@ export interface Config {
skipCookieDomainSecurity: boolean;
/** If true, single sign-on will be enabled */
enableSingleSignOn: boolean;
/** If true, signups via single sign-on will be allowed, overriding allowSignups */
allowSignupsViaSingleSignOn: boolean;
/** The Discovery URL (AKA Issuer) of the identity/single sign-on provider */
singleSignOnUrl: string;
/** The attribute to prefer as email of the identity/single sign-on provider */