-
-
Notifications
You must be signed in to change notification settings - Fork 141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi-factor authentication #796
base: master
Are you sure you want to change the base?
Conversation
@ozsay since it's pretty hard to have a generic |
} | ||
|
||
private async createMfaLoginProcess(user: User): Promise<MFALoginResult> { | ||
const loginToken = generateRandomToken(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ozsay I don't exactly understand why do we need this loginToken
, can you explain a bit why is it really needed please? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the user can only log in if he has the loginToken
which is provided to him upon successfully completing the challenge.
Did I over-think this? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the mfaToken is already a random token I feel like having this loginToken
does not add more security no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
following auth0
, the last step of the mfa is to call oauth/token
(in our case loginWithService
) with a token. I can't use the mfaToken
here because it's provided to the client before it does the challenge. that's why I have the extra token.
if we want, we can skip this step and have the performMfaChallenge
create the session and return the access and refresh tokens.
Codecov Report
@@ Coverage Diff @@
## master #796 +/- ##
========================================
Coverage ? 95.5%
========================================
Files ? 83
Lines ? 1937
Branches ? 401
========================================
Hits ? 1850
Misses ? 79
Partials ? 8
Continue to review full report at Codecov.
|
@@ -21,6 +21,7 @@ const start = async () => { | |||
const accountsDb = new DatabaseManager({ | |||
sessionStorage: userStorage, | |||
userStorage, | |||
mfaLoginAttemptsStorage: userStorage, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure mfaLoginAttemptsStorage
is optional so that it would not break the current usages. Also no need for it when MFA is not configured
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pr looks good 👍
A few comments I think would be great before merging.
- Do we want to have the ability to limit the sms code to 3 attempts for example?
- I will try to see if I can bypass the loginToken to see if we really need it or not :)
- should the authenticator api a part of this pull request or better to split in another one?
mfaChallenges
as a an array of string sounds not really extendable, where will you store the number associated to the user or other?- on the client isn't it better to return the mfaToken to the user instead of setting it in the local storage?
@@ -1 +1,11 @@ | |||
export const LoginResult = {}; | |||
|
|||
export const LoginWithServiceResult = { | |||
__resolveType(obj: any) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we type this one with the generated types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure what you mean
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using any import the types generated by graphql-codegen
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i hope I understood you correctly :)
52ac287
@@ -25,7 +25,7 @@ export class AccountsClientPassword { | |||
/** | |||
* Log the user in with a password. | |||
*/ | |||
public async login(user: any): Promise<LoginResult> { | |||
public async login(user: any): Promise<LoginResult | Omit<MFALoginResult, 'mfaToken'>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we return the mfaToken there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mainly, to simplify the usage
|
||
import { Configuration } from './types/configuration'; | ||
|
||
export class DatabaseManager implements DatabaseInterface { | ||
private userStorage: DatabaseInterface; | ||
private sessionStorage: DatabaseInterface | DatabaseInterfaceSessions; | ||
private mfaLoginAttemptsStorage?: DatabaseInterface | DatabaseInterfaceMfaLoginAttempts; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a required field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davidyaha pointed out that people might not want mfa
at all, so I made it optional
I think it's related to the #776 PR. The
I don't think any changes are required to the authenticators API. I will test an alpha version this week, but I believe that it should work.
currently, it will allow authenticating with any of the challenges in the array (only 1 challenge completion is sufficient for login). We can extend it in the future for more complex scenarios. I think the basic scenarios are covered here.
I can return it but I don't see any client usage of it, that's why I've "hidden" it inside thanks for the comments @pradel :) |
Will this allow for 2-step authentication through email, or just SMS? Also is this in the docs yet 😈? |
@ozsay Are we missing something here? Could this be merged? |
@davidyaha I proposed another design for the MFA implementation which will allow us to support more authenticators. I posted the architecture design on slack and I started an implementation on this pr #813 |
bdf442b
to
3e57ec8
Compare
an alternative way for #777 of doing MFA following the
auth0
way.example login flow of user:
/loginWithService('password', { username, password })
sms
|email
|...
) to complete/performMfaChallenge('sms', mfaToken, { codeFromSms })
/loginWithService('mfa', { mfaLoginToken })
mfaLoginToken
and returns login response.db:
server:
rest api:
graphql api:
client: