www/apps/resources/references/mfa/interfaces/mfa.AuthMfaProvider/page.mdx
import { TypeList } from "docs-ui"
In this guide, you’ll learn how to create a Multi-Factor Authentication (MFA) Auth Module Provider and the methods you must implement in its main service.
An MFA Module Provider is a provider that implements the AuthMfaProvider interface. It allows you to integrate a new MFA provider into the Medusa Auth Module, enabling users to enroll and authenticate with MFA factors beyond the built-in TOTP provider.
Medusa provides a built-in Totp MFA Module Provider that you can use out-of-the-box. You can also override it or create your own custom MFA Module Provider by following the instructions in this guide.
<Note title="Creating a Recovery Code MFA Module Provider Instead?">Refer to the Create a Recovery Code MFA Module Provider guide instead.
</Note>As you implement your MFA Auth Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
If you need to refer to an existing implementation as an example, check the Totp MFA Module Provider in the Medusa repository.
Start by creating a new directory for your module provider.
If you're creating the module provider in a Medusa application, create it under the src/modules directory. For example, src/modules/my-mfa.
If you're creating the module provider in a plugin, create it under the src/providers directory. For example, src/providers/my-mfa.
The rest of this guide always uses the src/modules/my-mfa directory as an example.
Create the file src/modules/my-mfa/service.ts that holds the module provider's main service. It must implement the AuthMfaProvider interface imported from @medusajs/framework/types:
import { AuthMfaProvider } from "@medusajs/framework/types"
type Options = {
// define any options your provider needs here
}
class MyAuthMfaProviderService implements AuthMfaProvider {
// TODO implement methods
}
export default MyAuthMfaProviderService
The constructor allows you to access resources from the module's container using the first parameter, and the provider's options using the second parameter.
The Auth Module injects authMfaFactorService into your provider's container. This is the
data service for the AuthMfaFactor model, and you should store it as a class property so
the rest of the provider's methods can use it to retrieve and manage MFA factors (for
example, to look up enabled factors for an auth identity, or to create a pending factor
during setup).
If you're creating a client or establishing a connection with a third-party service, do it in the constructor.
import { AuthMfaProvider } from "@medusajs/framework/types"
import { Logger, ModulesSdkTypes } from "@medusajs/framework/types"
type InjectedDependencies = {
logger: Logger
authMfaFactorService: ModulesSdkTypes.IMedusaInternalService<any>
}
type Options = {
issuer?: string
}
class MyAuthMfaProviderService implements AuthMfaProvider {
static identifier = "my-mfa"
readonly method = MyAuthMfaProviderService.identifier
protected logger_: Logger
protected authMfaFactorService_: ModulesSdkTypes.IMedusaInternalService<any>
protected options_: Options
// assuming you're initializing a client
protected client
constructor (
{ logger, authMfaFactorService }: InjectedDependencies,
options: Options
) {
this.logger_ = logger
this.authMfaFactorService_ = authMfaFactorService
this.options_ = options
// assuming you're initializing a client
this.client = new Client(options)
}
// ...
}
export default MyAuthMfaProviderService
Every MFA auth module provider must have an identifier static property. The provider's ID
will be stored as mfa_{identifier}.
For example:
class MyAuthMfaProviderService implements AuthMfaProvider {
static identifier = "my-mfa"
// ...
}
The MFA method that the provider implements. For example, totp.
This method checks whether the auth identity has an enabled factor managed by this provider. The Auth Module uses it to determine whether a verification attempt should be routed to the provider.
Use the injected authMfaFactorService to look up factors for the auth identity,
scoped to this provider's method. You can alternatively check with third-party services if the provider manages factors remotely.
class MyAuthMfaProvider implements AuthMfaProvider {
// ...
async canVerifyForAuthIdentity(
data: { auth_identity_id: string },
sharedContext?: Context
): Promise<boolean> {
const [factor] = await this.authMfaFactorService_.list(
{
auth_identity_id: data.auth_identity_id,
provider: this.method,
status: "enabled",
},
{ select: ["id"] },
sharedContext
)
return !!factor
}
}
<TypeList types={[{"name":"data","type":"object","description":"The auth identity to check.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"auth_identity_id","type":"string","description":"The ID of the auth identity to check.","optional":false,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"Context","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type TManager, which is a typed parameter passed to the context to specify the type of the transactionManager.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type TManager, which is a typed parameter passed to the context to specify the type of the manager.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"string","description":"A string indicating the isolation level of the context. Possible values are READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, or SERIALIZABLE.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"boolean","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"string","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"string","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"runId","type":"string","description":"A string indicating the ID of the current run.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"IMessageAggregator","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"string","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"string","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"string","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"preventReleaseEvents","type":"boolean","description":"preventReleaseEvents","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isCancelling","type":"boolean","description":"A boolean value indicating whether the current workflow execution is being cancelled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"cancelingFromParentStep","type":"boolean","description":"Weither or not a sub workflow cancellation is being triggered from a parent step.\nIf true, the parent step will not be triggered by the sub workflow.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="canVerifyForAuthIdentity"/>
<TypeList types={[{"name":"Promise","type":"Promise<boolean>","optional":false,"defaultValue":"","description":"Whether the provider has an enabled factor for the auth identity.","expandable":false,"children":[{"name":"boolean","type":"boolean","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="canVerifyForAuthIdentity"/>
This method initiates the MFA setup flow for an auth identity. It typically creates a
pending factor and returns the data the user needs in order to complete the setup, such
as a TOTP secret and otpauth_url that can be rendered as a QR code.
The Auth Module uses this method when an auth identity starts enrolling into MFA with the provider.
To create the factor in a pending state within Medusa, use the injected authMfaFactorService and
set the status of the factor to "pending" when creating it. You can store any provider-specific data
needed for the setup in the factor's provider_metadata.
Alternatively, you can manage the entire setup flow with third-party services if the provider manages factors remotely.
In that case, make sure to return any data needed to complete the setup in the response, and to create a pending factor
in Medusa once the setup is verified in verifySetup.
class MyAuthMfaProvider implements AuthMfaProvider {
// ...
async start(
data: AuthTypes.AuthMfaStartDTO,
sharedContext?: Context
): Promise<AuthTypes.AuthMfaStartResponse> {
const secret = this.generateSecret_()
const factor = await this.authMfaFactorService_.create(
{
auth_identity_id: data.auth_identity_id,
provider: this.method,
status: "pending",
provider_metadata: { secret },
},
sharedContext
)
return {
mfa: await this.serializeFactor_(factor),
secret,
otpauth_url: this.buildOtpAuthUrl_(secret, data),
}
}
}
<TypeList types={[{"name":"data","type":"AuthMfaStartDTO","description":"The details of the MFA factor to set up.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"auth_identity_id","type":"string","description":"The ID of the authentication identity to set up multi-factor authentication (MFA) for.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"provider","type":"AuthMfaProvider","description":"The multi-factor authentication (MFA) provider to use.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"label","type":"string \| null","description":"Optional label for the multi-factor authentication (MFA) configuration.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"issuer","type":"string","description":"The issuer name for time-based one-time password (TOTP) apps.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"metadata","type":"Record<string, unknown> \| null","description":"Additional metadata for the multi-factor authentication (MFA) setup.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"Context","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type TManager, which is a typed parameter passed to the context to specify the type of the transactionManager.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type TManager, which is a typed parameter passed to the context to specify the type of the manager.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"string","description":"A string indicating the isolation level of the context. Possible values are READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, or SERIALIZABLE.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"boolean","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"string","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"string","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"runId","type":"string","description":"A string indicating the ID of the current run.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"IMessageAggregator","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"string","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"string","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"string","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"preventReleaseEvents","type":"boolean","description":"preventReleaseEvents","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isCancelling","type":"boolean","description":"A boolean value indicating whether the current workflow execution is being cancelled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"cancelingFromParentStep","type":"boolean","description":"Weither or not a sub workflow cancellation is being triggered from a parent step.\nIf true, the parent step will not be triggered by the sub workflow.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="start"/>
<TypeList types={[{"name":"Promise","type":"Promise<AuthMfaStartResponse>","optional":false,"defaultValue":"","description":"The data required to complete the MFA setup, including the pending factor.","expandable":false,"children":[{"name":"mfa","type":"AuthMfaDTO","description":"The created multi-factor authentication (MFA) configuration.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"id","type":"string","description":"The multi-factor authentication (MFA) configuration's ID.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"provider","type":"AuthMfaProvider","description":"The multi-factor authentication (MFA) provider used.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"status","type":"AuthMfaStatus","description":"The status of this multi-factor authentication (MFA) configuration.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"auth_identity_id","type":"string","description":"The ID of the authentication identity this multi-factor authentication (MFA) belongs to.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"auth_identity","type":"AuthIdentityDTO","description":"The authentication identity this multi-factor authentication (MFA) belongs to.","optional":true,"defaultValue":"","expandable":true,"children":[]},{"name":"metadata","type":"Record<string, unknown> \| null","description":"Additional metadata for the multi-factor authentication (MFA) configuration.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"secret","type":"string","description":"The secret key for manual time-based one-time password (TOTP) app setup.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"otpauth_url","type":"string","description":"The QR code URL for easy time-based one-time password (TOTP) app setup.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="start"/>
This method verifies a code submitted during an MFA challenge against the auth identity's enabled factor for this provider (for example, by checking a TOTP code against the secret stored on the factor).
Use the injected authMfaFactorService to retrieve the enabled factor for the auth identity,
then validate the code against the factor's provider-specific metadata.
You can alternatively verify the code with third-party services if the provider manages factors remotely.
class MyAuthMfaProvider implements AuthMfaProvider {
// ...
async verify(
data: { auth_identity_id: string; code: string },
sharedContext?: Context
): Promise<boolean> {
const [factor] = await this.authMfaFactorService_.list(
{
auth_identity_id: data.auth_identity_id,
provider: this.method,
status: "enabled",
},
{},
sharedContext
)
// assuming you have a `verifyCode_` method that verifies the code
return factor ? this.verifyCode_(factor, data.code) : false
}
}
<TypeList types={[{"name":"data","type":"object","description":"The auth identity and code to verify.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"auth_identity_id","type":"string","description":"The ID of the auth identity to verify the code for.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"code","type":"string","description":"The MFA code to verify.","optional":false,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"Context","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type TManager, which is a typed parameter passed to the context to specify the type of the transactionManager.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type TManager, which is a typed parameter passed to the context to specify the type of the manager.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"string","description":"A string indicating the isolation level of the context. Possible values are READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, or SERIALIZABLE.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"boolean","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"string","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"string","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"runId","type":"string","description":"A string indicating the ID of the current run.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"IMessageAggregator","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"string","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"string","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"string","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"preventReleaseEvents","type":"boolean","description":"preventReleaseEvents","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isCancelling","type":"boolean","description":"A boolean value indicating whether the current workflow execution is being cancelled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"cancelingFromParentStep","type":"boolean","description":"Weither or not a sub workflow cancellation is being triggered from a parent step.\nIf true, the parent step will not be triggered by the sub workflow.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="verify"/>
<TypeList types={[{"name":"Promise","type":"Promise<boolean>","optional":false,"defaultValue":"","description":"Whether the code was successfully verified.","expandable":false,"children":[{"name":"boolean","type":"boolean","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="verify"/>
This method verifies a code submitted during the MFA setup flow and, if valid, activates the pending factor.
The Auth Module uses this method after start to confirm that the user has correctly configured their authenticator before the factor is enabled.
You can retrieve the pending factor created in start using the injected authMfaFactorService,
verify the submitted code against the provider-specific metadata stored on the factor, and
then update the factor's status to "enabled" if the verification is successful.
Alternatively, you can manage the entire setup flow with third-party services if the provider manages factors remotely.
In that case, make sure to verify the setup with the third-party service in this method, and to update the factor's status
to "enabled" in Medusa once the setup is verified.
class MyAuthMfaProvider implements AuthMfaProvider {
// ...
async verifySetup(
data: AuthTypes.AuthMfaVerifyDTO,
sharedContext?: Context
): Promise<AuthTypes.AuthMfaDTO> {
const factor = await this.authMfaFactorService_.retrieve(
data.id,
{},
sharedContext
)
if (!this.verifyCode_(factor, data.code)) {
throw new Error("Invalid MFA code")
}
const enabled = await this.authMfaFactorService_.update(
{ id: factor.id, status: "enabled" },
sharedContext
)
return await this.serializeFactor_(enabled)
}
}
<TypeList types={[{"name":"data","type":"AuthMfaVerifyDTO","description":"The details of the factor and the code to verify.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"id","type":"string","description":"The ID of the multi-factor authentication (MFA) configuration to verify.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"code","type":"string","description":"The verification code from the multi-factor authentication (MFA) provider.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"auth_identity_id","type":"string","description":"The ID of the authentication identity this multi-factor authentication (MFA) belongs to.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"Context","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type TManager, which is a typed parameter passed to the context to specify the type of the transactionManager.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type TManager, which is a typed parameter passed to the context to specify the type of the manager.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"string","description":"A string indicating the isolation level of the context. Possible values are READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, or SERIALIZABLE.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"boolean","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"string","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"string","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"runId","type":"string","description":"A string indicating the ID of the current run.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"IMessageAggregator","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"string","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"string","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"string","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"preventReleaseEvents","type":"boolean","description":"preventReleaseEvents","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isCancelling","type":"boolean","description":"A boolean value indicating whether the current workflow execution is being cancelled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"cancelingFromParentStep","type":"boolean","description":"Weither or not a sub workflow cancellation is being triggered from a parent step.\nIf true, the parent step will not be triggered by the sub workflow.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="verifySetup"/>
<TypeList types={[{"name":"Promise","type":"Promise<AuthMfaDTO>","optional":false,"defaultValue":"","description":"The verified (and now enabled) MFA factor.","expandable":false,"children":[{"name":"id","type":"string","description":"The multi-factor authentication (MFA) configuration's ID.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"provider","type":"AuthMfaProvider","description":"The multi-factor authentication (MFA) provider used.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"status","type":"AuthMfaStatus","description":"The status of this multi-factor authentication (MFA) configuration.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"auth_identity_id","type":"string","description":"The ID of the authentication identity this multi-factor authentication (MFA) belongs to.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"auth_identity","type":"AuthIdentityDTO","description":"The authentication identity this multi-factor authentication (MFA) belongs to.","optional":true,"defaultValue":"","expandable":true,"children":[{"name":"id","type":"string","description":"The ID of the auth identity.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"provider_identities","type":"ProviderIdentityDTO[]","description":"The list of provider identities linked to the auth identity.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"app_metadata","type":"Record<string, unknown>","description":"Holds information related to the actor IDs tied to the auth identity.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"metadata","type":"Record<string, unknown> \| null","description":"Additional metadata for the multi-factor authentication (MFA) configuration.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="verifySetup"/>
Create the file src/modules/my-mfa/index.ts with the following content:
import { ModuleProvider, Modules } from "@medusajs/framework/utils"
import MyAuthMfaProviderService from "./service"
export default ModuleProvider(Modules.AUTH, {
services: [MyAuthMfaProviderService],
})
This exports the module provider's definition, indicating that the MyAuthMfaProviderService is the module provider's service.
To use your MFA Module Provider, add it to the mfa.providers array of the Auth Module in medusa-config.ts:
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/auth",
dependencies: [Modules.CACHE, ContainerRegistrationKeys.LOGGER],
options: {
mfa: {
encryption_key: process.env.AUTH_MFA_ENCRYPTION_KEY,
providers: [
{
// if module provider is in a plugin, use `plugin-name/providers/my-mfa`
resolve: "./src/modules/my-mfa",
// if you're overriding the totp provider, set this to "totp"
id: "my-mfa",
options: {
// provider options...
}
}
]
},
providers: [
{
resolve: "@medusajs/medusa/auth-emailpass",
id: "emailpass",
},
],
},
},
]
})
There are two ways to test out your MFA Module Provider: