Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Architecting an elegant login experience in a m...

Architecting an elegant login experience in a multi-account world

Avatar for Sam Wolfand

Sam Wolfand

August 09, 2018

Other Decks in Technology

Transcript

  1. USER LOGIN & CREATE WORKSPACE PROCESSES SUCCESS URL CREATE WORKSPACE

    WORKSPACE NAME CONFIRM EMAIL SIGNED IN EMAIL LOGIN URL CONFIRM EMAIL
  2. THE SOLUTION SSO TWO FACTOR AUTH ADMIN/GUEST [email protected] [email protected] SIGNED

    IN CREATE WORKSPACE SSO TWO FACTOR AUTH ADMIN/GUEST CONFIRM EMAIL SIGNED IN CREATE WORKSPACE CONFIRM EMAIL
  3. THE ANATOMY OF AN AUTH REQUEST CONFIRM EMAIL YES NO

    YES NO OPEN MULTI FACTOR AUTH YES NO OPEN PROMPT SIGN IN HAS ANY MFA ACCOUNTS? HAS MULTIPLE MFA ACCOUNTS? HAS ANY STANDARD AUTH ACCOUNTS? HAS PENDING AUTH WORKSPACES? OPEN PENDING AUTH SCREEN NO YES SHOW EMPTY STATE OPEN SLACK
  4. THE ANATOMY OF AN AUTH REQUEST CONFIRM EMAIL YES NO

    YES NO OPEN MULTI FACTOR AUTH YES NO OPEN PROMPT SIGN IN HAS ANY MFA ACCOUNTS? HAS MULTIPLE MFA ACCOUNTS? HAS ANY STANDARD AUTH ACCOUNTS? HAS PENDING AUTH WORKSPACES? OPEN PENDING AUTH SCREEN NO YES SHOW EMPTY STATE OPEN SLACK
  5. THE ANATOMY OF AN AUTH REQUEST CONFIRM EMAIL .YES. OPEN

    PROMPT SIGN IN HAS ANY ACCOUNTS? .NO. OPEN SLACK CONTAINS A STANDARD AUTH WORKSPACE? .YES. OPEN PENDING AUTH SCREEN .NO. HAS SINGLE ACCOUNT? NO WORKSPACE 
 SELECTION SCREEN
  6. THE SLACK ARCHITECTURE class LoginContract { interface Presenter : BasePresenter<View>

    public void fetchData(burritoId: String) } interface View : BaseView<Presenter> { public void displayData(burritos: Burritos) } }
  7. THE SLACK ARCHITECTURE class Presenter { service.login() .subscribe(new Subscriber<LoginResult>>() {

    @Overridee Ppublicdvoid onError(Throwable throwable) { view.showError(); } @Override public void onNext(Workspace workspace) { view.loadData(result.data); } } });
  8. TECHNICAL CHALLENGES @Override public void onNext(Workspace workspace) { if (workspace.exists())

    {
 // open empty state
 } else if (workspace.isMfaRequired()) {
 // open MFA screen } else { // Open standard auth screen } }
  9. TECHNICAL CHALLENGES sealed class FoundWorkspacesResult { class NoJoinableWorkspaces() : FoundWorkspacesResult()

    class SingleCurrentWorkspace() : FoundWorkspacesResult() class SingleOrg() : FoundWorkspacesResult())) class MultipleWorkspaces() : FoundWorkspacesResult() }
  10. TECHNICAL CHALLENGES service.login())) .map { workspaces -> 
 when {

    workspace.exists()) -> NoJoinableWorkspaces())) workspace.size == 1 -> SingleCurrentWorkspace())) workspace.isEnterprise() -> SingleOrg())) else -> MultipleWorkspaces())) } }
  11. THE REDESIGN .subscribe { foundWorkspacesResult ->> when (foundWorkspacesResult) { is

    NoJoinableWorkspaces ->> { view.openPendingInvitesActivity() } is SingleCurrentWorkspace ->> { view.joinWorkspace(JoinWorkspaceEvent.StandardAuth() } is SingleOrg ->> { view.openPromptSignInActivity() } is MultipleWorkspaces ->> { view.openWorkspaceSelectionActivity() } } }
  12. THE REDESIGN @Override public void onError(Throwable e) {
 String errorCode

    = ((ApiError) e)).getErrorCode() 
 switch (errorCode) { case MULTI_FACTOR_REQUIRED: toggleErrorMessage(false); view.requireTwoFactorAuth(); break; }
  13. THE REDESIGN onErrorReturn { response -> (response as ApiResponseError).let {

    when (it.errorCode) {
 ApiError.MULTI_FACTOR_REQUIRED -> MfaResult.MfaRequired())
 else -> MfaResult.Error(“Something went wrong!”) } } }
  14. WHY IT MATTERS - know your customer - small amounts

    of friction add up to large pain points - good enough is never great - use Kotlin