@contember/engine-tenant-api 2.2.0-alpha.5 → 2.2.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -0
- package/dist/development/index.cjs +2 -0
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +2 -0
- package/dist/development/index.js.map +1 -1
- package/dist/development/src/migrations/2026-09-04-120000-idp-disable-local-authentication.cjs +18 -0
- package/dist/development/src/migrations/2026-09-04-120000-idp-disable-local-authentication.cjs.map +1 -0
- package/dist/development/src/migrations/2026-09-04-120000-idp-disable-local-authentication.js +19 -0
- package/dist/development/src/migrations/2026-09-04-120000-idp-disable-local-authentication.js.map +1 -0
- package/dist/development/src/migrations/runner.cjs +3 -1
- package/dist/development/src/migrations/runner.cjs.map +1 -1
- package/dist/development/src/migrations/runner.js +3 -1
- package/dist/development/src/migrations/runner.js.map +1 -1
- package/dist/development/src/migrations/snapshot.cjs +2 -1
- package/dist/development/src/migrations/snapshot.cjs.map +1 -1
- package/dist/development/src/migrations/snapshot.js +2 -1
- package/dist/development/src/migrations/snapshot.js.map +1 -1
- package/dist/development/src/model/commands/idp/ClaimIdpRevalidationCommand.cjs +3 -7
- package/dist/development/src/model/commands/idp/ClaimIdpRevalidationCommand.cjs.map +1 -1
- package/dist/development/src/model/commands/idp/ClaimIdpRevalidationCommand.js +3 -7
- package/dist/development/src/model/commands/idp/ClaimIdpRevalidationCommand.js.map +1 -1
- package/dist/development/src/model/commands/idp/CreateIdpCommand.cjs +8 -2
- package/dist/development/src/model/commands/idp/CreateIdpCommand.cjs.map +1 -1
- package/dist/development/src/model/commands/idp/CreateIdpCommand.js +8 -2
- package/dist/development/src/model/commands/idp/CreateIdpCommand.js.map +1 -1
- package/dist/development/src/model/commands/idp/UpdateIdpCommand.cjs +6 -2
- package/dist/development/src/model/commands/idp/UpdateIdpCommand.cjs.map +1 -1
- package/dist/development/src/model/commands/idp/UpdateIdpCommand.js +6 -2
- package/dist/development/src/model/commands/idp/UpdateIdpCommand.js.map +1 -1
- package/dist/development/src/model/queries/idp/LocalAuthDisablingIdpsQuery.cjs +21 -0
- package/dist/development/src/model/queries/idp/LocalAuthDisablingIdpsQuery.cjs.map +1 -0
- package/dist/development/src/model/queries/idp/LocalAuthDisablingIdpsQuery.js +21 -0
- package/dist/development/src/model/queries/idp/LocalAuthDisablingIdpsQuery.js.map +1 -0
- package/dist/development/src/model/queries/idp/PersonIdentityProvidersQuery.cjs +1 -1
- package/dist/development/src/model/queries/idp/PersonIdentityProvidersQuery.cjs.map +1 -1
- package/dist/development/src/model/queries/idp/PersonIdentityProvidersQuery.js +1 -1
- package/dist/development/src/model/queries/idp/PersonIdentityProvidersQuery.js.map +1 -1
- package/dist/development/src/model/queries/idp/helpers.cjs +3 -3
- package/dist/development/src/model/queries/idp/helpers.cjs.map +1 -1
- package/dist/development/src/model/queries/idp/helpers.js +3 -3
- package/dist/development/src/model/queries/idp/helpers.js.map +1 -1
- package/dist/development/src/model/service/PasswordResetManager.cjs +5 -0
- package/dist/development/src/model/service/PasswordResetManager.cjs.map +1 -1
- package/dist/development/src/model/service/PasswordResetManager.js +5 -0
- package/dist/development/src/model/service/PasswordResetManager.js.map +1 -1
- package/dist/development/src/model/service/PasswordlessSignInManager.cjs +19 -0
- package/dist/development/src/model/service/PasswordlessSignInManager.cjs.map +1 -1
- package/dist/development/src/model/service/PasswordlessSignInManager.js +19 -0
- package/dist/development/src/model/service/PasswordlessSignInManager.js.map +1 -1
- package/dist/development/src/model/service/SignInManager.cjs +5 -0
- package/dist/development/src/model/service/SignInManager.cjs.map +1 -1
- package/dist/development/src/model/service/SignInManager.js +5 -0
- package/dist/development/src/model/service/SignInManager.js.map +1 -1
- package/dist/development/src/model/service/idp/PersonIdentityProviderManager.cjs +6 -0
- package/dist/development/src/model/service/idp/PersonIdentityProviderManager.cjs.map +1 -1
- package/dist/development/src/model/service/idp/PersonIdentityProviderManager.js +6 -0
- package/dist/development/src/model/service/idp/PersonIdentityProviderManager.js.map +1 -1
- package/dist/development/src/resolvers/mutation/idp/AddIDPMutationResolver.cjs +2 -1
- package/dist/development/src/resolvers/mutation/idp/AddIDPMutationResolver.cjs.map +1 -1
- package/dist/development/src/resolvers/mutation/idp/AddIDPMutationResolver.js +2 -1
- package/dist/development/src/resolvers/mutation/idp/AddIDPMutationResolver.js.map +1 -1
- package/dist/development/src/resolvers/mutation/idp/UpdateIDPMutationResolver.cjs +2 -1
- package/dist/development/src/resolvers/mutation/idp/UpdateIDPMutationResolver.cjs.map +1 -1
- package/dist/development/src/resolvers/mutation/idp/UpdateIDPMutationResolver.js +2 -1
- package/dist/development/src/resolvers/mutation/idp/UpdateIDPMutationResolver.js.map +1 -1
- package/dist/development/src/resolvers/mutation/idp/audit.cjs +1 -0
- package/dist/development/src/resolvers/mutation/idp/audit.cjs.map +1 -1
- package/dist/development/src/resolvers/mutation/idp/audit.js +1 -0
- package/dist/development/src/resolvers/mutation/idp/audit.js.map +1 -1
- package/dist/development/src/resolvers/mutation/person/PasswordlessMutationResolver.cjs +4 -0
- package/dist/development/src/resolvers/mutation/person/PasswordlessMutationResolver.cjs.map +1 -1
- package/dist/development/src/resolvers/mutation/person/PasswordlessMutationResolver.js +4 -0
- package/dist/development/src/resolvers/mutation/person/PasswordlessMutationResolver.js.map +1 -1
- package/dist/development/src/resolvers/mutation/person/SignInMutationResolver.cjs +2 -2
- package/dist/development/src/resolvers/mutation/person/SignInMutationResolver.cjs.map +1 -1
- package/dist/development/src/resolvers/mutation/person/SignInMutationResolver.js +2 -2
- package/dist/development/src/resolvers/mutation/person/SignInMutationResolver.js.map +1 -1
- package/dist/development/src/schema/tenant.graphql.cjs +28 -0
- package/dist/development/src/schema/tenant.graphql.cjs.map +1 -1
- package/dist/development/src/schema/tenant.graphql.js +28 -0
- package/dist/development/src/schema/tenant.graphql.js.map +1 -1
- package/dist/production/index.cjs +2 -0
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.js +2 -0
- package/dist/production/index.js.map +1 -1
- package/dist/production/src/migrations/2026-09-04-120000-idp-disable-local-authentication.cjs +18 -0
- package/dist/production/src/migrations/2026-09-04-120000-idp-disable-local-authentication.cjs.map +1 -0
- package/dist/production/src/migrations/2026-09-04-120000-idp-disable-local-authentication.js +19 -0
- package/dist/production/src/migrations/2026-09-04-120000-idp-disable-local-authentication.js.map +1 -0
- package/dist/production/src/migrations/runner.cjs +3 -1
- package/dist/production/src/migrations/runner.cjs.map +1 -1
- package/dist/production/src/migrations/runner.js +3 -1
- package/dist/production/src/migrations/runner.js.map +1 -1
- package/dist/production/src/migrations/snapshot.cjs +2 -1
- package/dist/production/src/migrations/snapshot.cjs.map +1 -1
- package/dist/production/src/migrations/snapshot.js +2 -1
- package/dist/production/src/migrations/snapshot.js.map +1 -1
- package/dist/production/src/model/commands/idp/ClaimIdpRevalidationCommand.cjs +3 -7
- package/dist/production/src/model/commands/idp/ClaimIdpRevalidationCommand.cjs.map +1 -1
- package/dist/production/src/model/commands/idp/ClaimIdpRevalidationCommand.js +3 -7
- package/dist/production/src/model/commands/idp/ClaimIdpRevalidationCommand.js.map +1 -1
- package/dist/production/src/model/commands/idp/CreateIdpCommand.cjs +8 -2
- package/dist/production/src/model/commands/idp/CreateIdpCommand.cjs.map +1 -1
- package/dist/production/src/model/commands/idp/CreateIdpCommand.js +8 -2
- package/dist/production/src/model/commands/idp/CreateIdpCommand.js.map +1 -1
- package/dist/production/src/model/commands/idp/UpdateIdpCommand.cjs +6 -2
- package/dist/production/src/model/commands/idp/UpdateIdpCommand.cjs.map +1 -1
- package/dist/production/src/model/commands/idp/UpdateIdpCommand.js +6 -2
- package/dist/production/src/model/commands/idp/UpdateIdpCommand.js.map +1 -1
- package/dist/production/src/model/queries/idp/LocalAuthDisablingIdpsQuery.cjs +21 -0
- package/dist/production/src/model/queries/idp/LocalAuthDisablingIdpsQuery.cjs.map +1 -0
- package/dist/production/src/model/queries/idp/LocalAuthDisablingIdpsQuery.js +21 -0
- package/dist/production/src/model/queries/idp/LocalAuthDisablingIdpsQuery.js.map +1 -0
- package/dist/production/src/model/queries/idp/PersonIdentityProvidersQuery.cjs +1 -1
- package/dist/production/src/model/queries/idp/PersonIdentityProvidersQuery.cjs.map +1 -1
- package/dist/production/src/model/queries/idp/PersonIdentityProvidersQuery.js +1 -1
- package/dist/production/src/model/queries/idp/PersonIdentityProvidersQuery.js.map +1 -1
- package/dist/production/src/model/queries/idp/helpers.cjs +3 -3
- package/dist/production/src/model/queries/idp/helpers.cjs.map +1 -1
- package/dist/production/src/model/queries/idp/helpers.js +3 -3
- package/dist/production/src/model/queries/idp/helpers.js.map +1 -1
- package/dist/production/src/model/service/PasswordResetManager.cjs +5 -0
- package/dist/production/src/model/service/PasswordResetManager.cjs.map +1 -1
- package/dist/production/src/model/service/PasswordResetManager.js +5 -0
- package/dist/production/src/model/service/PasswordResetManager.js.map +1 -1
- package/dist/production/src/model/service/PasswordlessSignInManager.cjs +19 -0
- package/dist/production/src/model/service/PasswordlessSignInManager.cjs.map +1 -1
- package/dist/production/src/model/service/PasswordlessSignInManager.js +19 -0
- package/dist/production/src/model/service/PasswordlessSignInManager.js.map +1 -1
- package/dist/production/src/model/service/SignInManager.cjs +5 -0
- package/dist/production/src/model/service/SignInManager.cjs.map +1 -1
- package/dist/production/src/model/service/SignInManager.js +5 -0
- package/dist/production/src/model/service/SignInManager.js.map +1 -1
- package/dist/production/src/model/service/idp/PersonIdentityProviderManager.cjs +6 -0
- package/dist/production/src/model/service/idp/PersonIdentityProviderManager.cjs.map +1 -1
- package/dist/production/src/model/service/idp/PersonIdentityProviderManager.js +6 -0
- package/dist/production/src/model/service/idp/PersonIdentityProviderManager.js.map +1 -1
- package/dist/production/src/resolvers/mutation/idp/AddIDPMutationResolver.cjs +2 -1
- package/dist/production/src/resolvers/mutation/idp/AddIDPMutationResolver.cjs.map +1 -1
- package/dist/production/src/resolvers/mutation/idp/AddIDPMutationResolver.js +2 -1
- package/dist/production/src/resolvers/mutation/idp/AddIDPMutationResolver.js.map +1 -1
- package/dist/production/src/resolvers/mutation/idp/UpdateIDPMutationResolver.cjs +2 -1
- package/dist/production/src/resolvers/mutation/idp/UpdateIDPMutationResolver.cjs.map +1 -1
- package/dist/production/src/resolvers/mutation/idp/UpdateIDPMutationResolver.js +2 -1
- package/dist/production/src/resolvers/mutation/idp/UpdateIDPMutationResolver.js.map +1 -1
- package/dist/production/src/resolvers/mutation/idp/audit.cjs +1 -0
- package/dist/production/src/resolvers/mutation/idp/audit.cjs.map +1 -1
- package/dist/production/src/resolvers/mutation/idp/audit.js +1 -0
- package/dist/production/src/resolvers/mutation/idp/audit.js.map +1 -1
- package/dist/production/src/resolvers/mutation/person/PasswordlessMutationResolver.cjs +4 -0
- package/dist/production/src/resolvers/mutation/person/PasswordlessMutationResolver.cjs.map +1 -1
- package/dist/production/src/resolvers/mutation/person/PasswordlessMutationResolver.js +4 -0
- package/dist/production/src/resolvers/mutation/person/PasswordlessMutationResolver.js.map +1 -1
- package/dist/production/src/resolvers/mutation/person/SignInMutationResolver.cjs +2 -2
- package/dist/production/src/resolvers/mutation/person/SignInMutationResolver.cjs.map +1 -1
- package/dist/production/src/resolvers/mutation/person/SignInMutationResolver.js +2 -2
- package/dist/production/src/resolvers/mutation/person/SignInMutationResolver.js.map +1 -1
- package/dist/production/src/schema/tenant.graphql.cjs +28 -0
- package/dist/production/src/schema/tenant.graphql.cjs.map +1 -1
- package/dist/production/src/schema/tenant.graphql.js +28 -0
- package/dist/production/src/schema/tenant.graphql.js.map +1 -1
- package/dist/tests/cases/integration/mocked/gql/passwordless.d.ts +3 -0
- package/dist/tests/cases/integration/mocked/gql/passwordless.d.ts.map +1 -1
- package/dist/tests/cases/integration/mocked/localAuthenticationDisabled.test.d.ts +2 -0
- package/dist/tests/cases/integration/mocked/localAuthenticationDisabled.test.d.ts.map +1 -0
- package/dist/tests/cases/integration/mocked/sql/localAuthDisablingIdpsSql.d.ts +10 -0
- package/dist/tests/cases/integration/mocked/sql/localAuthDisablingIdpsSql.d.ts.map +1 -0
- package/dist/tests/cases/integration/mocked/sql/personIdentityProvidersSql.d.ts +1 -0
- package/dist/tests/cases/integration/mocked/sql/personIdentityProvidersSql.d.ts.map +1 -1
- package/dist/tests/cases/unit/idp/claimIdpRevalidation.test.d.ts +2 -0
- package/dist/tests/cases/unit/idp/claimIdpRevalidation.test.d.ts.map +1 -0
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/migrations/2026-09-04-120000-idp-disable-local-authentication.d.ts +3 -0
- package/dist/types/migrations/2026-09-04-120000-idp-disable-local-authentication.d.ts.map +1 -0
- package/dist/types/migrations/runner.d.ts.map +1 -1
- package/dist/types/migrations/snapshot.d.ts.map +1 -1
- package/dist/types/model/commands/idp/ClaimIdpRevalidationCommand.d.ts.map +1 -1
- package/dist/types/model/commands/idp/CreateIdpCommand.d.ts.map +1 -1
- package/dist/types/model/commands/idp/UpdateIdpCommand.d.ts.map +1 -1
- package/dist/types/model/queries/idp/LocalAuthDisablingIdpsQuery.d.ts +18 -0
- package/dist/types/model/queries/idp/LocalAuthDisablingIdpsQuery.d.ts.map +1 -0
- package/dist/types/model/queries/idp/PersonIdentityProvidersQuery.d.ts +1 -0
- package/dist/types/model/queries/idp/PersonIdentityProvidersQuery.d.ts.map +1 -1
- package/dist/types/model/queries/idp/helpers.d.ts +1 -1
- package/dist/types/model/queries/idp/helpers.d.ts.map +1 -1
- package/dist/types/model/queries/idp/index.d.ts +1 -0
- package/dist/types/model/queries/idp/index.d.ts.map +1 -1
- package/dist/types/model/queries/idp/types.d.ts +1 -0
- package/dist/types/model/queries/idp/types.d.ts.map +1 -1
- package/dist/types/model/service/PasswordResetManager.d.ts.map +1 -1
- package/dist/types/model/service/PasswordlessSignInManager.d.ts.map +1 -1
- package/dist/types/model/service/SignInManager.d.ts.map +1 -1
- package/dist/types/model/service/idp/PersonIdentityProviderManager.d.ts.map +1 -1
- package/dist/types/model/type/IdentityProvider.d.ts +2 -0
- package/dist/types/model/type/IdentityProvider.d.ts.map +1 -1
- package/dist/types/resolvers/mutation/idp/AddIDPMutationResolver.d.ts.map +1 -1
- package/dist/types/resolvers/mutation/idp/UpdateIDPMutationResolver.d.ts.map +1 -1
- package/dist/types/resolvers/mutation/idp/audit.d.ts.map +1 -1
- package/dist/types/resolvers/mutation/person/PasswordlessMutationResolver.d.ts.map +1 -1
- package/dist/types/schema/index.d.ts +33 -4
- package/dist/types/schema/index.d.ts.map +1 -1
- package/dist/types/schema/tenant.graphql.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/migrations/2026-09-04-120000-idp-disable-local-authentication.ts +18 -0
- package/src/migrations/runner.ts +2 -0
- package/src/migrations/snapshot.ts +2 -1
- package/src/model/commands/idp/ClaimIdpRevalidationCommand.ts +8 -7
- package/src/model/commands/idp/CreateIdpCommand.ts +7 -1
- package/src/model/commands/idp/UpdateIdpCommand.ts +5 -1
- package/src/model/queries/idp/LocalAuthDisablingIdpsQuery.ts +41 -0
- package/src/model/queries/idp/PersonIdentityProvidersQuery.ts +2 -0
- package/src/model/queries/idp/helpers.ts +3 -2
- package/src/model/queries/idp/index.ts +1 -0
- package/src/model/queries/idp/types.ts +1 -0
- package/src/model/service/PasswordResetManager.ts +9 -1
- package/src/model/service/PasswordlessSignInManager.ts +23 -1
- package/src/model/service/SignInManager.ts +9 -1
- package/src/model/service/idp/PersonIdentityProviderManager.ts +8 -0
- package/src/model/type/IdentityProvider.ts +2 -0
- package/src/resolvers/mutation/idp/AddIDPMutationResolver.ts +1 -0
- package/src/resolvers/mutation/idp/UpdateIDPMutationResolver.ts +1 -0
- package/src/resolvers/mutation/idp/audit.ts +1 -0
- package/src/resolvers/mutation/person/PasswordlessMutationResolver.ts +7 -0
- package/src/resolvers/mutation/person/SignInMutationResolver.ts +2 -2
- package/src/schema/index.ts +29 -0
- package/src/schema/tenant.graphql.ts +28 -0
- package/tests/cases/integration/mocked/gql/passwordless.ts +10 -0
- package/tests/cases/integration/mocked/idpClaimMappingConfig.test.ts +4 -4
- package/tests/cases/integration/mocked/localAuthenticationDisabled.test.ts +182 -0
- package/tests/cases/integration/mocked/personIdentityProviders.test.ts +67 -0
- package/tests/cases/integration/mocked/signInAnomalyDetection.test.ts +11 -0
- package/tests/cases/integration/mocked/signInIdpClaimMapping.test.ts +14 -0
- package/tests/cases/integration/mocked/signInIdpClaimMappingFixes.test.ts +4 -0
- package/tests/cases/integration/mocked/signInIdpClaimMappingSafety.test.ts +6 -0
- package/tests/cases/integration/mocked/signInIdpMutation.test.ts +5 -0
- package/tests/cases/integration/mocked/signInMfaEnforcement.test.ts +7 -0
- package/tests/cases/integration/mocked/signInMutation.test.ts +14 -0
- package/tests/cases/integration/mocked/signInPasswordlessMutation.test.ts +36 -0
- package/tests/cases/integration/mocked/sql/getIdpBySlugSql.ts +1 -1
- package/tests/cases/integration/mocked/sql/localAuthDisablingIdpsSql.ts +21 -0
- package/tests/cases/integration/mocked/sql/personIdentityProvidersSql.ts +3 -1
- package/tests/cases/integration/mocked/trustForwardedClientInfo.test.ts +3 -0
- package/tests/cases/unit/idp/claimIdpRevalidation.test.ts +50 -0
- package/tests/cases/unit/idp/idpManagerUnknownConfigKeys.test.ts +8 -1
|
@@ -4,6 +4,7 @@ import { testUuid } from '../../../src/testUuid.js'
|
|
|
4
4
|
import { selectMembershipsSql } from './sql/selectMembershipsSql.js'
|
|
5
5
|
import { signInMutation } from './gql/signIn.js'
|
|
6
6
|
import { getPersonByEmailSql } from './sql/getPersonByEmailSql.js'
|
|
7
|
+
import { localAuthDisablingIdpsSql } from './sql/localAuthDisablingIdpsSql.js'
|
|
7
8
|
import { SignInErrorCode } from '../../../../src/schema/index.js'
|
|
8
9
|
import { expect, test } from 'bun:test'
|
|
9
10
|
import { OtpAuthenticator } from '../../../../src/index.js'
|
|
@@ -31,6 +32,7 @@ test('signs in', async () => {
|
|
|
31
32
|
getConfigSql(),
|
|
32
33
|
getNextLoginAttemptSql(email),
|
|
33
34
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [] } }),
|
|
35
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
34
36
|
getAuthPoliciesSql(),
|
|
35
37
|
getConfigSql(),
|
|
36
38
|
getIdentityByIdSql({ identityId }),
|
|
@@ -94,6 +96,7 @@ test('signs in - normalize email', async () => {
|
|
|
94
96
|
getNextLoginAttemptSql(email),
|
|
95
97
|
getPersonByEmailSql({ email, response: null }),
|
|
96
98
|
getPersonByEmailSql({ email: 'john@doe.com', response: { personId, identityId, password, roles: [] } }),
|
|
99
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
97
100
|
getAuthPoliciesSql(),
|
|
98
101
|
getConfigSql(),
|
|
99
102
|
getIdentityByIdSql({ identityId }),
|
|
@@ -137,6 +140,7 @@ test('sign in - invalid password', async () => {
|
|
|
137
140
|
getConfigSql(),
|
|
138
141
|
getNextLoginAttemptSql(email),
|
|
139
142
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [] } }),
|
|
143
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
140
144
|
],
|
|
141
145
|
return: {
|
|
142
146
|
data: {
|
|
@@ -170,6 +174,7 @@ test('sign in - email not verified', async () => {
|
|
|
170
174
|
email,
|
|
171
175
|
response: { personId, identityId, password, roles: [], emailVerificationRequired: true, emailVerifiedAt: null },
|
|
172
176
|
}),
|
|
177
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
173
178
|
],
|
|
174
179
|
return: {
|
|
175
180
|
data: {
|
|
@@ -205,6 +210,7 @@ test('sign in - email verification required but already verified', async () => {
|
|
|
205
210
|
email,
|
|
206
211
|
response: { personId, identityId, password, roles: [], emailVerificationRequired: true, emailVerifiedAt: now },
|
|
207
212
|
}),
|
|
213
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
208
214
|
getAuthPoliciesSql(),
|
|
209
215
|
getConfigSql(),
|
|
210
216
|
getIdentityByIdSql({ identityId }),
|
|
@@ -253,6 +259,7 @@ test('otp token not provided', async () => {
|
|
|
253
259
|
otpUri: 'otpauth://totp/contember:john?secret=ABCDEFG&period=30&digits=6&algorithm=SHA1&issuer=contember',
|
|
254
260
|
},
|
|
255
261
|
}),
|
|
262
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
256
263
|
],
|
|
257
264
|
return: {
|
|
258
265
|
data: {
|
|
@@ -290,6 +297,7 @@ test('sign in - invalid otp token', async () => {
|
|
|
290
297
|
getConfigSql(),
|
|
291
298
|
getNextLoginAttemptSql(email),
|
|
292
299
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [], otpUri: otp.uri } }),
|
|
300
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
293
301
|
],
|
|
294
302
|
return: {
|
|
295
303
|
data: {
|
|
@@ -327,6 +335,7 @@ test('sign in - valid otp token', async () => {
|
|
|
327
335
|
getConfigSql(),
|
|
328
336
|
getNextLoginAttemptSql(email),
|
|
329
337
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [], otpUri: otp.uri } }),
|
|
338
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
330
339
|
getConfigSql(),
|
|
331
340
|
getIdentityByIdSql({ identityId }),
|
|
332
341
|
getAuthPoliciesSql(),
|
|
@@ -380,6 +389,7 @@ test('sign in - valid backup code (when OTP is required)', async () => {
|
|
|
380
389
|
getConfigSql(),
|
|
381
390
|
getNextLoginAttemptSql(email),
|
|
382
391
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [], otpUri: otp.uri } }),
|
|
392
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
383
393
|
consumeBackupCodeSql({ personId, codeHash: BACKUP_CODE_HASH, consumed: true }),
|
|
384
394
|
countUnusedBackupCodesSql({ personId, count: 4 }),
|
|
385
395
|
getConfigSql(),
|
|
@@ -432,6 +442,7 @@ test('sign in - email OTP enabled, no code provided: dispatches a code and retur
|
|
|
432
442
|
getConfigSql(),
|
|
433
443
|
getNextLoginAttemptSql(email),
|
|
434
444
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [], emailOtpEnabled: true } }),
|
|
445
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
435
446
|
// signIn lazily re-fetches the config to resolve the email-OTP send rate limit.
|
|
436
447
|
getConfigSql(),
|
|
437
448
|
...sendEmailOtpSql({ personId, rateLimitEventId: testUuid(1), tokenId: testUuid(2) }),
|
|
@@ -475,6 +486,7 @@ test('sign in - email OTP enabled, valid code: signs in', async () => {
|
|
|
475
486
|
getConfigSql(),
|
|
476
487
|
getNextLoginAttemptSql(email),
|
|
477
488
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [], emailOtpEnabled: true } }),
|
|
489
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
478
490
|
getLatestEmailOtpTokenSql({ personId, tokenId }),
|
|
479
491
|
claimEmailOtpAttemptSql({ tokenId }),
|
|
480
492
|
consumeEmailOtpTokenSql({ tokenId }),
|
|
@@ -515,6 +527,7 @@ test('sign in - email OTP enabled, invalid code: INVALID_OTP_TOKEN', async () =>
|
|
|
515
527
|
getConfigSql(),
|
|
516
528
|
getNextLoginAttemptSql(email),
|
|
517
529
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [], emailOtpEnabled: true } }),
|
|
530
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
518
531
|
getLatestEmailOtpTokenSql({ personId, tokenId }),
|
|
519
532
|
claimEmailOtpAttemptSql({ tokenId }),
|
|
520
533
|
],
|
|
@@ -550,6 +563,7 @@ test('sign in - already-used backup code is rejected', async () => {
|
|
|
550
563
|
getConfigSql(),
|
|
551
564
|
getNextLoginAttemptSql(email),
|
|
552
565
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [], otpUri: otp.uri } }),
|
|
566
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
553
567
|
consumeBackupCodeSql({ personId, codeHash: BACKUP_CODE_HASH, consumed: false }),
|
|
554
568
|
],
|
|
555
569
|
return: {
|
|
@@ -6,6 +6,7 @@ import { signInPasswordlessMutation } from './gql/passwordless.js'
|
|
|
6
6
|
import { computeTokenHash } from '../../../../src/index.js'
|
|
7
7
|
import { sqlTransaction } from './sql/sqlTransaction.js'
|
|
8
8
|
import { getPersonByIdSql } from './sql/getPersonByIdSql.js'
|
|
9
|
+
import { localAuthDisablingIdpsSql } from './sql/localAuthDisablingIdpsSql.js'
|
|
9
10
|
import { getConfigSql } from './sql/getConfigSql.js'
|
|
10
11
|
import { createSessionKeySql } from './sql/createSessionKeySql.js'
|
|
11
12
|
import { getIdentityProjectsSql } from './sql/getIdentityProjectsSql.js'
|
|
@@ -54,6 +55,7 @@ test('signInPasswordless - marks an unverified email verified before issuing the
|
|
|
54
55
|
personId,
|
|
55
56
|
response: { personId, identityId, password: '123', roles: [], email: 'john@doe.com', emailVerifiedAt: null },
|
|
56
57
|
}),
|
|
58
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
57
59
|
{
|
|
58
60
|
sql: SQL`UPDATE "tenant"."person_token" SET "used_at" = ? WHERE "id" = ? AND "used_at" IS NULL`,
|
|
59
61
|
parameters: [isDate, requestId],
|
|
@@ -110,6 +112,7 @@ test('signInPasswordless - skips the email-verified update when already verified
|
|
|
110
112
|
personId,
|
|
111
113
|
response: { personId, identityId, password: '123', roles: [], email: 'john@doe.com', emailVerifiedAt: now },
|
|
112
114
|
}),
|
|
115
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
113
116
|
{
|
|
114
117
|
sql: SQL`UPDATE "tenant"."person_token" SET "used_at" = ? WHERE "id" = ? AND "used_at" IS NULL`,
|
|
115
118
|
parameters: [isDate, requestId],
|
|
@@ -144,3 +147,36 @@ test('signInPasswordless - skips the email-verified update when already verified
|
|
|
144
147
|
},
|
|
145
148
|
})
|
|
146
149
|
})
|
|
150
|
+
|
|
151
|
+
test('signInPasswordless - a link created while the magic link was in flight still refuses the session', async () => {
|
|
152
|
+
const requestId = testUuid(1)
|
|
153
|
+
const personId = testUuid(2)
|
|
154
|
+
const identityId = testUuid(3)
|
|
155
|
+
const token = 'passwordless-token'
|
|
156
|
+
await executeTenantTest({
|
|
157
|
+
query: signInPasswordlessMutation({ requestId, token, validationType: 'token' }),
|
|
158
|
+
executes: [
|
|
159
|
+
...sqlTransaction(
|
|
160
|
+
personTokenByIdSql({ requestId, personId, token }),
|
|
161
|
+
getPersonByIdSql({
|
|
162
|
+
personId,
|
|
163
|
+
response: { personId, identityId, password: '123', roles: [], email: 'john@doe.com', emailVerifiedAt: now },
|
|
164
|
+
}),
|
|
165
|
+
localAuthDisablingIdpsSql({ personId, slugs: ['corporateSso'] }),
|
|
166
|
+
),
|
|
167
|
+
],
|
|
168
|
+
return: {
|
|
169
|
+
data: {
|
|
170
|
+
signInPasswordless: {
|
|
171
|
+
ok: false,
|
|
172
|
+
error: { code: 'IDP_REQUIRED' },
|
|
173
|
+
result: null,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
expectedAuthLog: {
|
|
178
|
+
type: 'passwordless_login',
|
|
179
|
+
response: expect.objectContaining({ ok: false }),
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
})
|
|
@@ -7,7 +7,7 @@ export const getIdpBySlugSql = (args: {
|
|
|
7
7
|
response: null | IdentityProviderRow
|
|
8
8
|
}): ExpectedQuery => ({
|
|
9
9
|
sql:
|
|
10
|
-
SQL`select "id", "slug", "type", "configuration", "disabled_at" as "disabledAt", "auto_sign_up" as "autoSignUp", "exclusive", "init_returns_config" as "initReturnsConfig", "require_verified_email" as "requireVerifiedEmail", "assume_email_verified" as "assumeEmailVerified"
|
|
10
|
+
SQL`select "id", "slug", "type", "configuration", "disabled_at" as "disabledAt", "auto_sign_up" as "autoSignUp", "exclusive", "init_returns_config" as "initReturnsConfig", "require_verified_email" as "requireVerifiedEmail", "assume_email_verified" as "assumeEmailVerified", "disable_local_authentication" as "disableLocalAuthentication"
|
|
11
11
|
from "tenant"."identity_provider"
|
|
12
12
|
where "slug" = ?`,
|
|
13
13
|
parameters: [args.slug],
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ExpectedQuery } from '@contember/database-tester'
|
|
2
|
+
import { SQL } from '../../../../src/tags.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The `LocalAuthDisablingIdpsQuery` lookup every local authentication path runs.
|
|
6
|
+
* `slugs` defaults to none, i.e. local authentication stays available.
|
|
7
|
+
*/
|
|
8
|
+
export const localAuthDisablingIdpsSql = (args: {
|
|
9
|
+
personId: string
|
|
10
|
+
slugs?: string[]
|
|
11
|
+
}): ExpectedQuery => ({
|
|
12
|
+
sql: SQL`SELECT "identity_provider"."slug" AS "slug"
|
|
13
|
+
FROM "tenant"."person_identity_provider"
|
|
14
|
+
INNER JOIN "tenant"."identity_provider" AS "identity_provider" ON "identity_provider"."id" = "person_identity_provider"."identity_provider_id"
|
|
15
|
+
WHERE "person_identity_provider"."person_id" = ? AND "identity_provider"."disable_local_authentication" = ? AND "identity_provider"."disabled_at" IS NULL
|
|
16
|
+
ORDER BY "identity_provider"."slug" ASC`,
|
|
17
|
+
parameters: [args.personId, true],
|
|
18
|
+
response: {
|
|
19
|
+
rows: (args.slugs ?? []).map(slug => ({ slug })),
|
|
20
|
+
},
|
|
21
|
+
})
|
|
@@ -8,6 +8,7 @@ export interface PersonIdpConnectionRow {
|
|
|
8
8
|
slug: string
|
|
9
9
|
type: string
|
|
10
10
|
disabledAt?: Date | null
|
|
11
|
+
disableLocalAuthentication?: boolean
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export const getPersonIdentityProvidersSql = (args: {
|
|
@@ -15,7 +16,7 @@ export const getPersonIdentityProvidersSql = (args: {
|
|
|
15
16
|
response: PersonIdpConnectionRow[]
|
|
16
17
|
}): ExpectedQuery => ({
|
|
17
18
|
sql:
|
|
18
|
-
SQL`SELECT "person_identity_provider"."id", "person_identity_provider"."created_at" as "createdAt", "person_identity_provider"."external_identifier" as "externalIdentifier", "identity_provider"."slug" as "identityProviderSlug", "identity_provider"."type" as "identityProviderType", "identity_provider"."disabled_at" as "identityProviderDisabledAt"
|
|
19
|
+
SQL`SELECT "person_identity_provider"."id", "person_identity_provider"."created_at" as "createdAt", "person_identity_provider"."external_identifier" as "externalIdentifier", "identity_provider"."slug" as "identityProviderSlug", "identity_provider"."type" as "identityProviderType", "identity_provider"."disabled_at" as "identityProviderDisabledAt", "identity_provider"."disable_local_authentication" as "identityProviderDisableLocalAuthentication"
|
|
19
20
|
FROM "tenant"."person_identity_provider"
|
|
20
21
|
INNER JOIN "tenant"."identity_provider" AS "identity_provider" ON "identity_provider"."id" = "person_identity_provider"."identity_provider_id"
|
|
21
22
|
WHERE "person_id" = ?
|
|
@@ -29,6 +30,7 @@ export const getPersonIdentityProvidersSql = (args: {
|
|
|
29
30
|
identityProviderSlug: it.slug,
|
|
30
31
|
identityProviderType: it.type,
|
|
31
32
|
identityProviderDisabledAt: it.disabledAt ?? null,
|
|
33
|
+
identityProviderDisableLocalAuthentication: it.disableLocalAuthentication ?? false,
|
|
32
34
|
})),
|
|
33
35
|
},
|
|
34
36
|
})
|
|
@@ -3,6 +3,7 @@ import { testUuid } from '../../../src/testUuid.js'
|
|
|
3
3
|
import { selectMembershipsSql } from './sql/selectMembershipsSql.js'
|
|
4
4
|
import { signInMutation } from './gql/signIn.js'
|
|
5
5
|
import { getPersonByEmailSql } from './sql/getPersonByEmailSql.js'
|
|
6
|
+
import { localAuthDisablingIdpsSql } from './sql/localAuthDisablingIdpsSql.js'
|
|
6
7
|
import { expect, test } from 'bun:test'
|
|
7
8
|
import { createSessionKeySql } from './sql/createSessionKeySql.js'
|
|
8
9
|
import { getIdentityProjectsSql } from './sql/getIdentityProjectsSql.js'
|
|
@@ -29,6 +30,7 @@ test('signIn: trustForwardedClientInfo=true is propagated when caller has the fl
|
|
|
29
30
|
getConfigSql(),
|
|
30
31
|
getNextLoginAttemptSql(email),
|
|
31
32
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [] } }),
|
|
33
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
32
34
|
getAuthPoliciesSql(),
|
|
33
35
|
getConfigSql(),
|
|
34
36
|
getIdentityByIdSql({ identityId }),
|
|
@@ -68,6 +70,7 @@ test('signIn: trustForwardedClientInfo=true is silently dropped when caller has
|
|
|
68
70
|
getConfigSql(),
|
|
69
71
|
getNextLoginAttemptSql(email),
|
|
70
72
|
getPersonByEmailSql({ email, response: { personId, identityId, password, roles: [] } }),
|
|
73
|
+
localAuthDisablingIdpsSql({ personId }),
|
|
71
74
|
getAuthPoliciesSql(),
|
|
72
75
|
getConfigSql(),
|
|
73
76
|
getIdentityByIdSql({ identityId }),
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
import { createConnectionMock, type ExpectedQuery } from '@contember/database-tester'
|
|
3
|
+
import { DatabaseContext, type Providers } from '../../../../src/index.js'
|
|
4
|
+
import { ClaimIdpRevalidationCommand } from '../../../../src/model/commands/idp/ClaimIdpRevalidationCommand.js'
|
|
5
|
+
|
|
6
|
+
const providers: Providers = {
|
|
7
|
+
bcrypt: async value => value,
|
|
8
|
+
bcryptCompare: async () => true,
|
|
9
|
+
now: () => new Date('2026-05-26T12:00:00Z'),
|
|
10
|
+
randomBytes: async length => Buffer.alloc(length),
|
|
11
|
+
uuid: () => '00000000-0000-0000-0000-000000000000',
|
|
12
|
+
decrypt: async value => ({ value, needsReEncrypt: false }),
|
|
13
|
+
encrypt: async value => ({ value, version: 1 }),
|
|
14
|
+
encryptionEnabled: true,
|
|
15
|
+
hash: value => Buffer.from(value.toString()),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const execute = async (expectedQuery: ExpectedQuery): Promise<boolean> => {
|
|
19
|
+
const expectedQueries = [expectedQuery]
|
|
20
|
+
const connection = createConnectionMock(expectedQueries)
|
|
21
|
+
const db = new DatabaseContext(connection.createClient('tenant', { module: 'tenant' }), providers)
|
|
22
|
+
|
|
23
|
+
const claimed = await db.commandBus.execute(new ClaimIdpRevalidationCommand('idp-session-1', '30 seconds'))
|
|
24
|
+
|
|
25
|
+
expect(expectedQueries).toHaveLength(0)
|
|
26
|
+
return claimed
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// The table must be schema-qualified: the tenant schema is not on the connection's search_path,
|
|
30
|
+
// so an unqualified name makes the claim fail on every request, and the caller treats that as
|
|
31
|
+
// "somebody else holds the claim" — re-validation then never runs at all.
|
|
32
|
+
test('the claim is issued against the qualified tenant table', async () => {
|
|
33
|
+
const claimed = await execute({
|
|
34
|
+
sql: 'update "tenant"."idp_session" set "last_validated_at" = now() where "id" = ? and "last_validated_at" <= now() - ?::interval',
|
|
35
|
+
parameters: ['idp-session-1', '30 seconds'],
|
|
36
|
+
response: { rowCount: 1 },
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
expect(claimed).toBe(true)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('a session still inside the throttle window is not claimed', async () => {
|
|
43
|
+
const claimed = await execute({
|
|
44
|
+
sql: 'update "tenant"."idp_session" set "last_validated_at" = now() where "id" = ? and "last_validated_at" <= now() - ?::interval',
|
|
45
|
+
parameters: ['idp-session-1', '30 seconds'],
|
|
46
|
+
response: { rowCount: 0 },
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
expect(claimed).toBe(false)
|
|
50
|
+
})
|
|
@@ -10,7 +10,14 @@ import { DatabaseContext } from '../../../../src/index.js'
|
|
|
10
10
|
// no network), so the real provider can be used here rather than a pass-through double.
|
|
11
11
|
|
|
12
12
|
const OIDC_BASE = { url: 'https://idp.example.com', clientId: 'client', clientSecret: 'secret' }
|
|
13
|
-
const OPTIONS = {
|
|
13
|
+
const OPTIONS = {
|
|
14
|
+
autoSignUp: false,
|
|
15
|
+
exclusive: false,
|
|
16
|
+
initReturnsConfig: false,
|
|
17
|
+
requireVerifiedEmail: false,
|
|
18
|
+
assumeEmailVerified: false,
|
|
19
|
+
disableLocalAuthentication: false,
|
|
20
|
+
}
|
|
14
21
|
|
|
15
22
|
const makeManager = () => {
|
|
16
23
|
const registry = new IDPHandlerRegistry()
|