supertokens / supertokens-core

Open source alternative to Auth0 / Firebase Auth / AWS Cognito
https://supertokens.com
Other
12.17k stars 476 forks source link

Make recipe enabled booleans better #979

Open sattvikc opened 2 months ago

sattvikc commented 2 months ago

🐛 Bug Report

Changes to the core:


Backend SDK changes:

Docs changes

Migration of tenant config:

Things to note:

Useful informations

(Write what happened. Add screenshots, stacktraces, videos, anything that can help)

TODO

rishabhpoddar commented 2 months ago

Using backend sdk with CDI 4.0 with new tenant creation using 4.0

When the backend SDK asks for tenant config, we will return the enabled booleans based on:

Using backend sdk with CDI 4.0 with new tenant creation using 5.1 (new to be CDI)

NOTE: We have to tell users that the new CDI will be breaking in how you create tenants. Instead of passing enabled booleans, you need to pass in firstFactors array ONLY. When the backend SDK asks for tenant config, we will return the enabled booleans based on:

Using backend sdk with CDI 5.0 with new tenant creation using 5.0

Using backend sdk with CDI 5.0 with new tenant creation using 5.1 (new to be CDI)

NOTE: We have to tell users that the new CDI will be breaking in how you create tenants. Instead of passing enabled booleans, you need to pass in firstFactors array ONLY.

Using backend sdk with CDI 5.1 (new CDI to be) with new tenant creation using 5.1 (new CDI to be)

Other changes:

sattvikc commented 1 month ago

Database state for tenant

Introduce new state [] (empty) for firstFactors and providers

{
    emailPasswordEnabled: boolean,
    passwordlessEnabled: boolean,
    thirdPartyEnabled: boolean,
    firstFactors?: null | string[], // null, empty, non-empty
    requiredSecondaryFactors: string[], // null, non-empty
    providers: null | Provider[] // null, empty, non-empty
}

Recipe enabled check in core

isEmailPasswordEnabled = emailPasswordEnabled || (firstFactors != null && firstFactors.includes('emailpassword')) || (requiredSecondaryFactors != null && requiredSecondaryFactors.includes('emailpassword'))
isThirdPartyEnabled = thirdPartyEnabled || (firstFactors != null && firstFactors.includes('thirdparty')) || (requiredSecondaryFactors != null && requiredSecondaryFactors.includes('thirdparty'))
isPasswordlessEnabled = passwordlessEnabled || (firstFactors != null && firstFactors.includesOneOf('otp-email', 'otp-phone', 'link-email', 'link-phone')) || (requiredSecondaryFactors != null && requiredSecondaryFactors.includesOneOf('otp-email', 'otp-phone', 'link-email', 'link-phone'))

In the above conditions, we have three parts with OR conditions:

Note: v2 means new endpoint for create/update and get/list tenants

New v2 endpoints for create/update CUD/app/tenant and list cud/apps/tenants and get tenant

Input for create/update tenant

{
  tenantId: string;
  firstFactors?: null | string[], // allows empty array
  requiredSecondaryFactors: null | string[], // does not allow empty array
  coreConfig: any,
}

Output of a tenant in get tenant or list tenants

{
  tenantId: string;
  firstFactors?: string[]; // undefined or empty or non-empty
  requiredSecondaryFactors?: string[]; // undefined or non-empty
  thirdParty: {
    providers?: Provider[]; // undefined or empty or non-empty
  };
  coreConfig: any;
}

Using Backend CDI 4.0, Create/Update tenant using core CDI 4.0

Using Backend CDI 4.0, Create/Update tenant using core CDI 5.0

Edge cases:

Using Backend CDI 4.0, Create/Update tenant using core CDI 5.1 (v2)

Using Backend CDI 5.0, Create/Update tenant using core CDI 5.0

Using Backend CDI 5.0, Create/Update tenant using core CDI 5.1 (v2)

Using Backend CDI 5.1 (v2), Create/Update tenant using core CDI 5.1 (v2)

Edge cases

DB schema changes

Migrating for 5.0 -> 5.1

From dashboard point of view

For migrated tenant from 4.0

firstFactors

thirdParty providers

For migrated tenant from 5.0

firstFactors

thirdParty providers

Tenants on 5.1

Use cases

  1. Using MFA with static config of firstFactors and requiredSecondary factors

    With Core migrated, but the SDK is not updated yet:

    The tenants have firstFactors set to null, which will remain as is post migration. This also means that the user would have enabled necessary recipes that are being used.

    • Effectively, core will allow all the enabled recipes, letting the first factor and secondary factors as expected.
    • Old deprecated API will return null for firstFactors and then SDK will use the static config as expected.

    With core migratied and the SDK updated:

    • the v2 APIs will still return null for firstFactors and effectively static config will be used
  2. Using MFA with firstFactors and requiredSecondaryFactors configured in the tenant

    With Core migrated, but the SDK is not updated yet:

    • firstFactors and requiredSecondaryFactors will return values as is and effectively they will be used in the SDK.
    • Core does not care about the recipe booleans and will allow all factors specified in firstFactors and requiredSecondaryFactors

    With core migratied and the SDK updated:

    • the v2 APIs will also return the same values as is and effectively they will be used in the SDK.
  3. Using dashboard to create new tenants, but there are also existing tenants and Backend is still on an older CDI:

    First Factors

    • The tenants created in 4.0 will have firstFactors set to null. Effectively this will use the static config. On switching any of the toggles, firstFactors will be updated and it will never get to null state from the dashboard.
      • if backend is using CDI 4.0, booleans will be based on the firstFactors since it's no more set to null. Original booleans will be ignored.
      • if backend is using CDI 5.0:
      • if using backend SDK, it does not depend on booleans, so the SDK will use the firstFactors as is
      • else, the recipe boolean set to true effectively means the recipe could be used for either first factor or the secondary factors. It should not be used as a first factor.
      • if backend is using CDI 5.1 and using deprecated APIs:
      • the behaviour will be same as 5.0
    • The tenants created using 5.0 with firstFactors and the recipe booleans, we only show first factors in the dashboard. Toggling any of it will only update the firstFactors and leave the booleans as is.
      • Backend behaviours will be similar to what is explained in the previous point

    Secondary Factors

    • The tenants created in 4.0 will have requiredSecondaryFactors set to null. Toggling them will update the requiredSecondaryFactors
      • if backend is using CDI 4.0, secondary factors aren't considered for anything
      • if backend is using CDI 5.0:
      • if using backend SDK, it does not depend on the booleans. just the value of requiredSecondaryFactors is considered.
      • else, the recipe boolean will show up as true if the factor is present in the requiredSecondaryFactors
      • if backend is using CDI 5.1 and using the deprecated APIs:
      • the behaviour will be same as 5.0
      • The tenants created using 5.0 with requiredSecondaryFactors and the recipe booleans, we only show secondary factors in the dashboard. Toggling any of it will only update the requiredSecondaryFactors and leave the booleans as is.
      • Backend behaviours will be similar to what is explained in the previous point

    Creation of new tenants

    • The new tenants will have firstFactors set to [] by default. Which means, all the first factors will be disabled by default. Toggling them just updates the firstFactors array. It will never get to null state.
      • For backend using CDI 4.0, the booleans will be based on firstFactors
sattvikc commented 1 month ago

database states

enabled booleans for latest node SDK: filtering out the first and second factors that have boolean set to false for py/go SDK: frontend uses subset of this as first factors based on what is initialised in the frontend static config

firstFactors null: for latest node SDK: use backend static config. It will throw an error on frontend if all the recipes from this array are not initialised for py/go SDK: implies all recipe booleans are true []: for latest node SDK: no login allowed for py/go SDK: all enabled booleans are false ["emailpassword", "thirdparty"]: for latest node SDK: it means we use subset of this based on which recipe is initialised in the backend static config. This will overwrite mfa.init(firstFactors) It will throw an error on frontend if all the recipes from this array are not initialised for py/go SDK: only enabled booleans associated with the factors are true, rest false. What is rendered on frontend is subset of this based on which recipe is initialised in the frontend static config

requiredSecondaryFactors null: for latest node SDK: MFA is not enabled for py/go SDK: not applicable ["otp-phone"]: for latest node SDK: enables subset based on backend SDK initialised recipes (check with Mihaly) for py/go SDK: not applicable

providers TODO

create or update in v4.0

create public tenant

Intention

create non-public tenant

Intention

{ emailPasswordEnabled: true }

Intention

{ emailPasswordEnabled: false }

Intention

similarly for thirdparty and passwordless

create or update in v5.0

create public tenant (same as v4)

Intention

create non-public tenant (same as v4)

Intention

{ emailPasswordEnabled: true }

Intention

{ emailPasswordEnabled: false }

Intention

{ firstFactors: null }

Intention

{ firstFactors: ["otp-phone"] }

Intention

{ emailPasswordEnabled: true, firstFactors: null, requiredSecondaryFactors: null }

{ requiredSecondaryFactors: null }

{ requiredSecondaryFactors : [...]}

create or update in v5.1 (v2 API)

{ firstFactors: null }

{ firstFactors: ['emailpassword']}

{ firstFactors: [] }

{ requiredSecondaryFactors: ['emailpassword'] }