# Login With Oasis

# Overview

In addition to storing sensitive data in your own Oasis accounts, you can also store data in your users' Oasis accounts, giving them full ownership, control, and visibility over their data.

To store data in user accounts, your users need to sign up for our Oasis Steward app (opens new window), after which you will need to ask them to authenticate themselves to your application. We support user authentication via OpenID Connect (opens new window), an identity layer on top of OAuth 2.0 (opens new window).

This tutorial will show you how to onboard users to Steward app with our Oasis Auth service.

Generally, you have two ways to use Oasis Auth:

  • You use it as a single way of authentication for your application.
  • You use it in combination with an existing authentication in your application in which case you want to link user's account in Oasis Auth with user's account in your existing authentication system. We recommend doing this linking as part of the onboarding process for your application.

Each user is represented by an identity on the Oasis platform. Our goal is to use an OAuth2 flow to retrieve the unique ID associated with the user's Parcel identity, and use it within your application.

# How It Works

We will implement the following authentication flow for your application:

  1. In the case of user linking, user first signs up for your (web) application. Otherwise, this step is skipped.
  2. User wants to manage their data through Oasis Steward.
  3. User is redirected to Steward.
  4. User signs up or logs in to Steward.
  5. On successful login and approval, User is redirected back to your application. The callback URL contains an authorization code.
  6. Your app exchanges the authorization code for an ID token.
  7. Your app obtains a Parcel ID from the ID token.

If you are not familiar with OAuth, this is a good time to read up on the basics (opens new window).

The OAuth standard supports many variants, called "flows". In this example, we are going to use the explicit PKCE authentication flow.

# Example App

In the remainder of this tutorial, we will walk through the code required to implement the above. The final, complete source code is available in our examples repository.

It is structured as follows:

  • public contains the user-facing web application, including the OIDC library we use to integrate with account linking service.
  • public/callback contains the callback landing page used for steps 5 and 6.
  • src contains a minimal backend server, meant to mimic how you might serve your application.

We use a few open source libraries to simplify our application:

To start the example, run npm i && npm start.

# Configuring the OIDC Client

Our simple webapp will consist of two static web pages (index.html and callback/index.html in the example) with some JavaScript that communicates with Oasis Auth authentication server.

We initialize the oidc-client using the following configuration:

const PARCEL_AUTH_URL = process.env.PARCEL_AUTH_URL ?? 'https://auth.oasislabs.com';

const oidcConfig = {
  authority: PARCEL_AUTH_URL,
  // Replace with your app's front-end client ID.
  client_id: process.env.ACME_FRONTEND_CLIENT_ID!,
  redirect_uri: `http://localhost:${port}/callback`,
  response_type: 'code',
  scope: 'openid',
  filterProtocolClaims: false,
  loadUserInfo: false,
  extraQueryParams: {
    audience: 'https://api.oasislabs.com/parcel',
  },
  extraTokenParams: {
    audience: 'https://api.oasislabs.com/parcel',
  },
};

Specifically:

  • authority and issuer should be https://auth.oasislabs.com.
  • client_id is the client ID of your app's front-end client. If you have not generated a front-end client yet, you can do so in the Parcel developer portal here (opens new window)
  • redirect_uri is the URL which user's browser will be redirected to, when authentication succeeds. We will use http://localhost:4050/callback since we will be locally running Node.js on port 4050 and answering /callback GET request. NOTE: Redirect URL must be listed in your client's redirect URLs list in Parcel Portal!
  • response_type should be code. This means our callback will receive the authorization code (i.e. the explicit authentication flow).
  • scope is a list of user information your application would like to access. We set this to openid which provides our application the user's Parcel ID.
  • extraQueryParams and extraTokenParams must contain at least audience parameter key with value https://api.oasislabs.com/parcel.

# Redirecting to Steward App

When the user visits our app, we first initialize oidc-client with the configuration above:

<script src="/getOidcConfig" type="text/javascript"></script>
<script>
  Oidc.Log.logger = console;
  Oidc.Log.level = Oidc.Log.DEBUG;
  console.log(oidcConfig)
  const oidcClient = new Oidc.UserManager(oidcConfig);
</script>

We then give our users a bit of context around what Oasis is and why they should take control of their data:

<h2>Data Privacy</h2>
<p>
  Here at ACME ltd., your privacy is very important to us. We've
  partnered with Oasis Labs so you can own your recipe data from our
  app.
</p>
<p>To set up your Oasis account, click Get Started</p>
<button onclick="oidcClient.signinRedirect()" data-cy="sign-up-oasis">
  Get started with Oasis
</button>
<button>Maybe later</button>

When the user clicks the button, they are redirected to Steward app, where they can sign up or log in.

Once logged in to Steward, your user will be prompted to share their newly minted Oasis identity with your application:

steward_share_identity_info

# Exchanging the Authorization Code

Based on the configuration we supplied to oidc-client, we need to be ready to receive the authorization code at https://localhost:4050/callback. We will do this solely in front-end by simply serving the callback/index.html file.

Then, we exchange the authorization code for the id_token using the oidcClient.processSigninResponse method.








 












<script src="/getOidcConfig" type="text/javascript"></script>
<script>
  Oidc.Log.logger = console;
  Oidc.Log.level = Oidc.Log.DEBUG;
  const oidcClient = new Oidc.OidcClient(oidcConfig);
  (async function () {
    try {
      const response = await oidcClient.processSigninResponse(location.href)
      const IDToken = response.id_token;
      const decoded = jwt_decode(IDToken);
      const userId = decoded.sub;
      console.log(`ID token:\n${JSON.stringify(decoded, null, '')}`);
      document.getElementById('user-id').innerText = `${userId}`;
      document.getElementById('result').hidden = false
    } catch (error) {
      document.getElementById('result').hidden = true;
      document.getElementById('error').innerText = `${error}`;
    }
  })()
</script>

Once we receive a response, we pull out the id_token:









 











<script src="/getOidcConfig" type="text/javascript"></script>
<script>
  Oidc.Log.logger = console;
  Oidc.Log.level = Oidc.Log.DEBUG;
  const oidcClient = new Oidc.OidcClient(oidcConfig);
  (async function () {
    try {
      const response = await oidcClient.processSigninResponse(location.href)
      const IDToken = response.id_token;
      const decoded = jwt_decode(IDToken);
      const userId = decoded.sub;
      console.log(`ID token:\n${JSON.stringify(decoded, null, '')}`);
      document.getElementById('user-id').innerText = `${userId}`;
      document.getElementById('result').hidden = false
    } catch (error) {
      document.getElementById('result').hidden = true;
      document.getElementById('error').innerText = `${error}`;
    }
  })()
</script>

And use the jwt-decode library to decode the user's ID:










 










<script src="/getOidcConfig" type="text/javascript"></script>
<script>
  Oidc.Log.logger = console;
  Oidc.Log.level = Oidc.Log.DEBUG;
  const oidcClient = new Oidc.OidcClient(oidcConfig);
  (async function () {
    try {
      const response = await oidcClient.processSigninResponse(location.href)
      const IDToken = response.id_token;
      const decoded = jwt_decode(IDToken);
      const userId = decoded.sub;
      console.log(`ID token:\n${JSON.stringify(decoded, null, '')}`);
      document.getElementById('user-id').innerText = `${userId}`;
      document.getElementById('result').hidden = false
    } catch (error) {
      document.getElementById('result').hidden = true;
      document.getElementById('error').innerText = `${error}`;
    }
  })()
</script>

# Using the Identity

We can confirm the user identity is correct by displaying it in our app:

<div id="result" hidden>
  <span>Your user id is</span>
  <button id="user-id" data-cy="user-id"></button>
</div>

If we go back to Steward app (opens new window) and click on our name, we should see the same ID in the menu.

You can view the full example in the Parcel Examples repository.