SoftwareBrothers / adminjs

AdminJS is an admin panel for apps written in node.js
https://adminjs.co
MIT License
8.19k stars 662 forks source link

[Feature]: 2FA on login? #1621

Open ivictbor opened 7 months ago

ivictbor commented 7 months ago

Description

Hey guys, do you have ideas how to quickly implement 2FA (Google Authenticator/Authy) 6-digits code on regular login?

We see there is no such feature out of the box, but interesting whether you have some hooks or interceptors to implement custom step in login form.

Suggested Solution

-

Alternatives

No response

Additional Context

No response

SarikAnwar01 commented 7 months ago

https://www.npmjs.com/package/speakeasy does this help? @ivictbor

ivictbor commented 7 months ago

https://www.npmjs.com/package/speakeasy does this help? @ivictbor

Thanks a lot, yes, this package looks nice, my cocern is more how to injext a page with a 2FA code after adminjs login page, is there facility to inject custom components on login?

AshotN commented 5 months ago

You need to override the login component

https://docs.adminjs.co/ui-customization/writing-your-own-components componentLoader.override('Login', './Login/index')

Then you have to create your own login provider

 const baseRouter = AdminJSKoa.buildAuthenticatedRouter(admin, adminJSApp, {
    provider: adminJSAuthProvider,
...

Something like this


interface AuthPayload extends DefaultAuthenticatePayload {
  OTPToken: string
}

class AuthProvider extends BaseAuthProvider {
  protected readonly authenticate = async (payload: AuthPayload, context: Koa.ParameterizedContext) => {
    const { email, password, OTPToken } = payload

    const res = getUserLogic(email, password, OTPToken)

    if (!res) return null
    const { user } = res
    if (user.role !== USER_ROLES.ADMIN) throw new ForbiddenError()

    return {
      email: user.email,
      title: `${user.first_name} ${user.last_name}`,
      role: user.role,
      }
    } as CurrentAdmin
  }

  constructor({}) {
    super()
  }

  override async handleLogin(opts: LoginHandlerOptions, context: any) {
    const { data = {} } = opts
    const { email, password, OTPToken } = data

    return this.authenticate({ email, password, OTPToken }, context)
  }
}

export const adminJSAuthProvider = new AuthProvider({
  componentLoader
})

This should get you on the right path, AdminJS is not very well documented. But in general you can override most components and parts of it's logic. So there is a lot to gain by poking around the source code when you want to change something.