Sign In With Open Loop Access

Direct Authentication Example 2

In this example, a fictitious app called InstantAutoPay uses the direct authentication API calls to sign in an existing user with email address ex1@example.com.

Prerequisites

Step 1

Login and Password Form

The user enters their email address (or SMS number) and password on the first form. In the API, the fields are called login and password.

Step 2

The user entered ex1@example.com in the login field and jellydonut in the password field. The app calls POST /aa/signin.

Full API Call URL
https://api.wingcash.com/aa/signin
API Call Documentation
POST /aa/signin
Headers
Content-Type: application/json
Request body
{
    "device_uuid": "907fb623-a4a9-4b59-b952-ad783bea7246",
    "client_id": "4954253560",
    "login": "ex1@example.com",
    "password": "jellydonut"
}
cURL example
curl https://api.wingcash.com/aa/signin \
    -H 'Content-Type: application/json' \
    -d '{"device_uuid": "907fb623-a4a9-4b59-b952-ad783bea7246",
        "client_id": "4954253560",
        "login": "ex1@example.com",
        "password": "jellydonut"}'
Response status code
200
Response body
{
    "attempt_path":"/aa/4399844113/",
    "authenticated":{
        "email:ex1@example.com":{
            "country":null,
            "original":"ex1@example.com",
            "strong":true,
            "used_password":true
        }
    },
    "captcha_required":false,
    "code_length":6,
    "completed_mfa":false,
    "factor_id":"99b1f585",
    "invite_id":null,
    "profile_id":"4356518574",
    "profile_title":"Jacques Black",
    "revealed_codes":[
        "146847 => phone:+12025551111"
    ],
    "secret":"b5JmjbEwYBtit9raLEJhj0LukIM",
    "signup":null,
    "trust30":false,
    "unauthenticated":{
        "phone:+12025551111":{
            "country":"US",
            "original":"(202) 555-1111"
        }
    }
}

The login was recognized and the password was correct, so the response is an AuthnResult object that contains Initial Attributes, Code Entry Attributes, and some State Attributes. It does not have any Final Attributes yet.

The AuthnResult includes attempt_path and secret, the Initial Attributes of an AuthnResult. The path and secret will be used for the rest of the calls in this authentication flow. The path and secret should be used for one flow only. The app should have no need to store the attempt_path or secret in a persistent (non-volatile) way.

The AuthnResult also includes factor_id, code_length, unauthenticated, and revealed_codes, the Code Entry Attributes of an AuthnResult. These attributes suggest that the app should ask the user to enter the code they received at the email address specified by the unauthenticated attribute, which is ex1@example.com. Because this example is based on a sandbox instance of OPN, the revealed_codes attribute is present in the AuthnResult, revealing the secret code sent to the user (as a convenience for development and testing.) That attribute is not available in production.

Finally, the AuthnResult includes captcha_required and trust30, some of the State Attributes of an AuthnResult. They indicate that the user has not recently tried to abuse the service, so no CAPTCHA is currently required, and the user has not indicated that they want to trust the device they’re using for 30 days.

At the same time, the platform sends an email to ex1@example.com, telling the user the code they should enter. The email contains the text similar to the following (unless the message template has been customized for the site):

Hi Jacques Black,

Welcome back to InstantAutoPay! Use the following code to continue signing in.

146847

This email contains private information, so please do not forward or share it. If you have any questions, email us at support@instantautopay.example.com.

Step 3

The app uses its implementation of the Authentication Decision Tree to decide what to do next.

  1. Because the AuthnResult has no completed_mfa attribute, the expression !result.completed_mfa evaluates as true and the app continues into the first conditional block of the tree.
  2. Because the AuthnResult has a non-empty factor_id attribute, the expression !result.factor_id && !result.profile_id evaluates as false and the app skips the corresponding conditional block.
  3. Because the AuthnResult has a non-empty factor_id attribute, the expression result.factor_id evaluates as true and the app executes the corresponding conditional block.
  4. The app sets the next_interaction variable to enter-code and finishes the decision tree.

Code Entry Form

Because the next_interaction is enter-code, the app shows a form that asks the user to enter the code they received in email. The form may show some helpful hints provided by the AuthnResponse:

  • The code is 6 digits in length (from code_length)
  • The code was sent to ex1@example.com (from unauthenticated)

As shown in the decision tree comments, the app should call POST /aa/(string:id)/auth-uid when the user completes the form.

Step 4

The user enters the code and the app submits the code to the POST /aa/(string:id)/auth-uid API call. In production, the app will not know whether the code is correct until the API call responds.

Full API Call URL
https://api.wingcash.com/aa/4399844113/auth-uid
API Call Documentation
POST /aa/(string:id)/auth-uid
Headers
Authorization: opn secret="b5JmjbEwYBtit9raLEJhj0LukIM" Content-Type: application/json
Request body
{
    "factor_id": "99b1f585",
    "code": "{{example2_code}}",
}
cURL example
curl https://api.wingcash.com/aa/4399844113/auth-uid \
    -H 'Authorization: opn secret="b5JmjbEwYBtit9raLEJhj0LukIM"' \
    -H 'Content-Type: application/json' \
    -d '{"factor_id": "99b1f585", "code": "{{example2_code}}"}'
Response status code
200
Response body
{
    "authenticated":{
        "email:ex1@example.com":{
            "country":null,
            "original":"ex1@example.com",
            "strong":true,
            "used_password":true
        },
        "phone:+12025551111":{
            "country":"US",
            "original":"(202) 555-1111",
            "strong":false,
            "used_password":false
        }
    },
    "captcha_required":false,
    "completed_mfa":true,
    "invite_id":null,
    "profile":{
        "accepted_national_currencies":[],
        "address":"",
        "address_data":null,
        "chain_id":null,
        "first_name":"Jacques",
        "id":"4356518574",
        "image125":null,
        "image24":null,
        "image25":null,
        "image250w":null,
        "image48":null,
        "image50":null,
        "image73":null,
        "is_individual":true,
        "last_name":"Black",
        "latitude":"",
        "longitude":"",
        "phone":"",
        "preferred_currency":"",
        "support_paycode":false,
        "title":"Jacques Black",
        "unsupported_national_currencies":[],
        "url":"https://wingcash.com/p/4356518574/",
        "username":null,
        "wingcash_uid":"wingcash:4356518574"
    },
    "profile_id":"4356518574",
    "profile_title":"Jacques Black",
    "signup":null,
    "token":{
        "access_token":"t4954253560-6938285940-j6lZuSJxIppCXyUoyoo2JWuG1OM",
        "expires_in":899,
        "hard_expires_in":316223999,
        "scope":"accept_offer change_settings edit_account list_friends manage_account manage_sent mobile_device send_cash send_to_account view view_full_history view_history view_wallet",
        "token_type":"bearer"
    },
    "trust30":false
}

The AuthnResult has State Attributes along with the Final Attributes, token and profile.

Step 5

The app uses its implementation of the Authentication Decision Tree to decide what to do next.

  1. Because the completed_mfa attribute is true, the expression !result.completed_mfa evaluates as false and the app skips the first top-level conditional block of the decision tree.
  2. Because the AuthnResult has a profile_id, the expression !result.profile_id evaluates as false and the app skips the second top-level conditional block as well.
  3. The app sets the next_interaction variable to authenticated and finishes the decision tree.

Authentication Complete

The user is now authenticated, so the app proceeds to its main view. The access token (to be used for most other API calls) is in the access_token sub-attribute of the AuthnResult’s token attribute.