Skip to content
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

[discuss] multi-step-authentication #777

Closed
wants to merge 2 commits into from

Conversation

ozsay
Copy link
Contributor

@ozsay ozsay commented Aug 21, 2019

we have a 2 step login scenario that is not supported currently by accounts:

  1. user logs-in with username and password.
  2. after successful password authentication, we send an SMS with code to the user to be authenticated with (other PR to support login with code).

only after these 2 steps a user is logged in.

This Authentication strategy supports this (and more) scenario.

The Authenticator accepts 2 or more other Authentication services which are being authenticated one after another. finally, after all authentication steps are successful, a token is created to perform the actual login.

  • implement service
  • add unit tests
  • API docs

@davidyaha FYI

@codecov
Copy link

codecov bot commented Aug 21, 2019

Codecov Report

Merging #777 into master will increase coverage by 0.17%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #777      +/-   ##
==========================================
+ Coverage   95.15%   95.32%   +0.17%     
==========================================
  Files          80       84       +4     
  Lines        1774     1841      +67     
  Branches      348      362      +14     
==========================================
+ Hits         1688     1755      +67     
  Misses         78       78              
  Partials        8        8
Impacted Files Coverage Δ
packages/multi-step/src/accounts-multi-step.ts 100% <100%> (ø)
packages/multi-step/src/index.ts 100% <100%> (ø)
packages/multi-step/src/errors.ts 100% <100%> (ø)
packages/multi-step/src/utils/crypto.ts 100% <100%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update dca8132...42ff231. Read the comment docs.

2 similar comments
@codecov
Copy link

codecov bot commented Aug 21, 2019

Codecov Report

Merging #777 into master will increase coverage by 0.17%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #777      +/-   ##
==========================================
+ Coverage   95.15%   95.32%   +0.17%     
==========================================
  Files          80       84       +4     
  Lines        1774     1841      +67     
  Branches      348      362      +14     
==========================================
+ Hits         1688     1755      +67     
  Misses         78       78              
  Partials        8        8
Impacted Files Coverage Δ
packages/multi-step/src/accounts-multi-step.ts 100% <100%> (ø)
packages/multi-step/src/index.ts 100% <100%> (ø)
packages/multi-step/src/errors.ts 100% <100%> (ø)
packages/multi-step/src/utils/crypto.ts 100% <100%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update dca8132...42ff231. Read the comment docs.

@codecov
Copy link

codecov bot commented Aug 21, 2019

Codecov Report

Merging #777 into master will increase coverage by 0.17%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #777      +/-   ##
==========================================
+ Coverage   95.15%   95.32%   +0.17%     
==========================================
  Files          80       84       +4     
  Lines        1774     1841      +67     
  Branches      348      362      +14     
==========================================
+ Hits         1688     1755      +67     
  Misses         78       78              
  Partials        8        8
Impacted Files Coverage Δ
packages/multi-step/src/accounts-multi-step.ts 100% <100%> (ø)
packages/multi-step/src/index.ts 100% <100%> (ø)
packages/multi-step/src/errors.ts 100% <100%> (ø)
packages/multi-step/src/utils/crypto.ts 100% <100%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update dca8132...42ff231. Read the comment docs.

Copy link
Member

@davidyaha davidyaha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good but really needs an example to showcase how to use it.

this.db = store;
}

public async authenticateStep({ index, serviceId, ...params }: StepParams): Promise<StepResult> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ozsay Not sure I understand how this works. Could you write a small flow summary in a comment?
Who calls authenticateStep?

Copy link
Contributor Author

@ozsay ozsay Aug 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently, it's very much abstract due to changes required for us in graphql-api.

But the idea is that the client will call it.

let's say we want to make a two-factor authentication with username-password and code received from SMS.

so the flow is like this (considering the changes here #776):

  1. client will call authenticateStep({index: 0, username: 'abc', password: '123'}) and will receive: { nextStep: 1, serviceId: '1234' }
  2. client will call prepareAuthentication from the code service to send an SMS with a code.
  3. after he got the SMS he can authenticate the next step: authenticateStep({index: 1, code: '4455', serviceId: '1234'}) and will receive the loginToken: {serviceId: '1234', token: '...'}
  4. then he can perform the regular loginWithService({ serviceName: 'multi-step-authentication', params: { serviceId: '1234', token: '...' } }).

Copy link
Member

@davidyaha davidyaha Aug 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay, so this way we the client is actually oblivious to how many authentication steps there are? is there something in the response that indicates to the client that he should activate SMS rather then something like an authenticator app?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the client should be aware of the authentication steps since I can't predict here the flow of the authentication (can involve SMS sending, EMAIL...)

return hash.digest('hex');
}

export function createToken(): { token: string; hash: string } {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ozsay Is that the token we send back to the client?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@pradel
Copy link
Member

pradel commented Aug 23, 2019

@ozsay thanks for starting working on the multi-step authentication. I want this feature for quite some time inside accounts-js but never had time to start the implementation.

I took some time to try to think about the best implementation possible. Currently we have 2fa with OTP code only for passwords but the way it's done is not flexible at all and limited to the password service.

My approach was to make this multi-factor step in the core of the server package, no need to have a separate package for it. I really like the way auth0 implemented this:

  • client sends a request to the server with email + password. If the user does not have MFA enabled then return a new session. If the user has MFA enabled then return a challenge to resolve (sms, email, other...). I think we could use a graphql union to describe the return of the authenticate function (either session or challenge).
  • client calls a new mutation to resolve the challenge and if the challenge is successful the server returns a new session.

You can read more about it there https://auth0.com/docs/multifactor-authentication/api/challenges.

Would love to have your thoughts about this :).

@ozsay
Copy link
Contributor Author

ozsay commented Aug 26, 2019

@pradel thanks for sharing your thoughts on this PR.

I guess that following what is done in auth0 makes sense and seems like it may simplify clients code.

do you have any technical followup of how to do it? it will require changes in User. where do you think the current MFA state (which challenges are remaining to resolve) should be stored (locally/db)?

@pradel
Copy link
Member

pradel commented Aug 26, 2019

@ozsay do you want to join the slack channel? Might be easier to discuss the implementation :)

@ozsay
Copy link
Contributor Author

ozsay commented Aug 26, 2019

sure why not :)

@pradel
Copy link
Member

pradel commented Aug 26, 2019

invited

@ozsay ozsay mentioned this pull request Sep 4, 2019
11 tasks
@ozsay
Copy link
Contributor Author

ozsay commented Sep 6, 2019

closing it for the better method in #796

@ozsay ozsay closed this Sep 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants