Skip to main content

Creating a new user

caution

The integration libraries and documentation for Yat are still in Alpha. They are not yet feature complete, and there are likely bugs in the implementation.

Overview#

There are 2 paths we can use for integration, with the user-centric path being more “privacy” focused, and the organization-centric path being more centralized.

In the user-centric approach, each user will register independently and we will return access / refresh tokens to the calling app which can be used independently to update the users’ Yats.

In the organization-centric approach, the user will be tied to an organization, giving the organization certain control over their Yats. This would require a centralized server that would route all requests on behalf of the user.

Signing up a new user#

To create a new account at y.at, register a user with a POST query to the /users end-point.

There are three ways to register a new account on y.at:

  • With an email only. A magic link is sent to the user via email to complete their registration. The user's email address is marked confirmed as soon as the user has clicked the magic link provided in their email. After clicking the link the user is asked for a password to complete their registration.
  • With an email and password. Registration is immediate. Logins may use either username-password. An initial email confirmation step where the user must click an provided confirmation magic link is required to allow access to checkout to complete any yat purchases.
  • With an "alternate id" and a password. Affiliates and y.at partners will use this approach. The registration and flow is the same as for email/password. With "alternate id" registrations, logins must always use username-password semantics, since there is no email address to receive a magic link.

An example code snippet for registering a new account is given in Integrating Yats - Registering a new account.

What is the alternate_id?#

Users can be registered using either to their email or an alternate_id. The latter is a global unique identifier, which is typically deterministically determined from the user's integration application and device. Such users require the source and password fields to be set when registering. To provide a deamless user experience, the password is also typically determined under the hood by applying a hash function to data unique to the user and her device.

When configured this way, users registered via alternate_id can only be authenticated via password, and from the app that carried out the registration (since the user doesn't know their password).

Users can supply their email address in the web interface at any time to complete their profile and enable email-based authentication.

Authentication flows#

Following are 2 distinct authentication flows:

On top of that user account might be enabled with two factor authentication, which would additionally require to pass 2FA code validation.

Conventional login via password can be used with user accounts registered to their alternate_id, whilst for users registered to their email magic link is preferred way.

POST to /auth/token would result in refresh_token, access_token and a flag if two factor authentication is required:

  • refresh_token is a token which only allows to acquire access_token via POST to /auth/token/refresh
  • access_token is a regular JWT token used to authenticate all the API calls providing it in the Authorization HTTP Header concatenated after the "Bearer " string.
  • requires_2fa is a flag which indicates if further authentication via 2FA is required, see section below for details.

We will skip example again as it was already provided in the previous section Integrating Yats - User authentication.

Two factor authentication#

Users that setup their account with two factor authentication (2FA) via POST /account/2fa, followed by confirmation to POST /account/2fa/confirm will have an extra step during authentication and login.

When 2FA is required, the requires_2fa will indicate what type of 2FA is required. For example,

{  "requires_2fa": "GoogleAuthenticator"  ...}

You can obtain a refresh_token by posting the 2FA code via POST to /auth/2fa. This token can be used as a regular refresh token.

Even after logging in, some sensitive endpoints are marked with 2FA. These endpoints require two factor authentication validation after a certain grace period (usually 15 minutes). This would be indicated with 424 HTTP response code FAILED DEPENDENCY.

When this happens, a new access_token should be acquired by submitting a new code to /auth/2fa before proceeding with request.

The Yat SDK manages most of the flow for you as can be seen from the example below:

const yat = require('yatjs');const { authenticator } = require('otplib');const api = new yat.YatJs();authenticator.options = { encoding: 'hex' };
let alternate_id = 'my-app-user-id-' + Math.random();let password = 'secret-password';let SECRET = '';/** * Register a new Yat account * @returns {Promise<boolean>} */async function register() {    try {        let res = await api.users().createUser({            'first_name': "Testy",            'last_name': "McTesty",            'source': "My nice app",            'alternate_id': alternate_id,            'password': password,        });        return true;    } catch (err) {        const alreadyRegistered = err.status === 422 && err.body.fields.alternate_id && err.body.fields.alternate_id[0].code === "uniqueness";        if (!alreadyRegistered) {            console.log(`Could not register an account: `, err);        }        return alreadyRegistered;    }}
/** * Setup account with 2FA enabled * @returns {Promise<boolean>} */async function register_with_2fa() {    try {        if (!await register()) {            console.log("Account already registered, will try to login");        }        await api.login(alternate_id, password);        let { ga_secret, ga_qr_code_svg } = await api.users().enable2FA({"provider": "GoogleAuthenticator"});        // NOTE: qr_code_svg is svg in text which should be shown to user to save in Google Authenticator        // For the API purposes we will be using secret directly        SECRET = ga_secret;        let code = authenticator.generate(ga_secret);        console.log(`Confirming 2FA with ${code}. Secret ${ga_secret}`);        await api.users().confirm2FA({code});        console.log("Confirmed 2FA for user account. Logged out.");        api.logout();        return true;    } catch (err) {        console.log(`Could not setup 2FA for account: `, err);        return false;    }}
// Basic login demoasync function runDemo() {    api.basePath = 'http://localhost:3001';    console.log(`Yat API calls will be made to ${api.basePath}`);    if (!await register_with_2fa()) return;    try {        let res = await api.login(alternate_id, password);        console.log("Before confirm_2fa: Requires 2FA = ", res.requires_2fa);        let code = authenticator.generate(SECRET);        res = await api.confirm_2fa(code);        console.log("After confirm_2fa: Requires 2FA = ", res.requires_2fa);        let account = await api.users().getAccount();        console.log("User profile data:", account.user);    } catch (res) {        console.log(`Could not log in`, res);    }}
runDemo()    .then(() => console.log("Bye"))    .catch(console.error);

The script above would output:

Yat API calls will be made to http://localhost:3001Confirming 2FA with 966584. Secret 32JH3WX6T5MX6IAXDRC5TL4NXW5QZQTGConfirmed 2FA for user account. Logged out.Before confirm_2fa: Requires 2FA =  GoogleAuthenticatorAfter confirm_2fa: Requires 2FA =  nullUser profile data: CurrentUserUser {  created_at: 2021-11-04T17:15:12.311Z,  free_limit: 1,  id: '9959671c-e45b-4a01-9f7a-e90fda28375e',  pubkeys: [    'ac6eb8980ab34b68ad5894108736fab67b422864d0f5d6c7488202bee671f36e'  ],  remaining_free_emoji: 1,  role: 'User',  two_factor_should_prompt: false,  updated_at: 2021-11-04T17:15:12.771Z,  alternate_id: 'my-app-user-id-0.3491619342661756',  email: null,  email_verified_at: null,  first_name: 'Testy',  last_name: 'McTesty',  source: 'My nice app',  two_factor_auth: [ 'GoogleAuthenticator' ],  two_factor_last_prompted_at: null}Bye

Magic links#

What the heck are magic links? I'm glad you asked.

A user only requires an email address for registration. An introductory email is sent which includes a “magic link”. The calling app can register an intent for the Yat domain, which would trigger an email to the user with a login link to the y.at site. Once the user has associated a password magic links are no longer available for their use and they must use that password going forward. If the user signs up with an email address and password a magic link is sent their way to confirm their email address.