Skip to content

Identity

The frontend gets the current identity from the /.cratis/me endpoint. The backend derives that identity from the authenticated request, so what the frontend sees is what the server knows about the caller, not anything the browser stored. Only when the server has no such endpoint does the frontend fall back to a cookie; see upgrading from the identity cookie. Configure authentication on the host; identity lookup is not itself a login mechanism.

Important note: Since local development is not configured with the identity provider, but you still need a way to test that both the backend and the frontend deals with the identity in the correct way. This can be achieved by creating the correct token and injecting it as request headers using a browser extension. Read more about generating principals.

Identity is intended for read-only UI consumption through IdentityProvider. The identity is held in the page’s memory, where anyone with the browser’s developer tools can change it, so it is not proof of anything. Every protected backend operation must authorize the actual credential independently.

The IdentityProvider provides functionality for getting the current identity.

import { IdentityProvider } from '@cratis/arc/identity';
const identity = await IdentityProvider.getCurrent();
console.log(`Hello '${identity.name}'`);

Note that the getCurrent() method is an asynchronous operation that returns a promise. The first call asks /.cratis/me for the identity. The answer is kept in memory for the page, so later calls return it without asking the server again.

When the server does not resolve an identity, for example because the caller is not signed in, getCurrent() returns an identity with isSet set to false, and that answer is not kept: the next call asks the server again.

IdentityProvider.clearCache() forgets the identity kept in memory, so the next getCurrent() asks the server again, and expires a .cratis-identity cookie left by an earlier version. It does not revoke sessions or remove authentication tokens or cookies. Real sign-out must complete through your authentication system before reconnecting anonymously.

IdentityProvider.clearIdentityCookie() is deprecated and now does the same as clearCache().

Part of the identity can hold details that are beyond what the identity provider provides. These details are application specific and something that your application or ingress should be responsible for filling out. Details can be considered optional, as that might not be a requirement for your application.

The getCurrent() method takes a generic parameter that describes the expected details object. It does not guarantee the backend supplies one. Guard absent details or choose an explicit display fallback:

import { IdentityProvider } from '@cratis/arc/identity';
type IdentityDetails = {
department: string,
age: number
};
const identity = await IdentityProvider.getCurrent<IdentityDetails>();
const department = identity.details?.department ?? 'Unknown department';
console.log(`Hello '${identity.name}' from '${department}'`);

The return type coming from getCurrent() looks like the following:

NameTypeDescription
idstringThe unique identifier from the identity provider
namestringThe user name
rolesstring[]Array of roles the identity is in
detailsTDetailsApplication-specific details, default generic type object; data may be absent at runtime
isInRole(role: string) => booleanMethod to check if the identity is in a specific role

The identity includes information about the roles assigned to the user. You can check if a user is in a specific role using the isInRole() method:

import { IdentityProvider } from '@cratis/arc/identity';
const identity = await IdentityProvider.getCurrent();
if (identity.isInRole('Admin')) {
console.log('User is an admin');
}

You can also access the roles array directly:

import { IdentityProvider } from '@cratis/arc/identity';
const identity = await IdentityProvider.getCurrent();
console.log(`User roles: ${identity.roles.join(', ')}`);

In some scenarios you might need to refresh the identity. Typically if the user has been granted more access or details has been updated. Rather than having your user log out and back in again, you can issue a refresh. Refresh always calls /.cratis/me for the identity and details, and replaces what is kept in memory, so later calls to getCurrent() return the refreshed identity. It does not renew or revoke authentication credentials.

To refresh the identity you can call the refresh() method on the identity object itself.

import { IdentityProvider } from '@cratis/arc/identity';
let identity = await IdentityProvider.getCurrent();
identity = await identity.refresh();

The identity object is designed to be immutable, leading to the refresh() method having to return a new instance. This means that the original identity instance won’t be updated and you would have to replace it if you have it as a variable.

Earlier versions of Arc wrote the identity to a JavaScript-readable .cratis-identity cookie, and IdentityProvider read it before asking /.cratis/me. Now IdentityProvider asks /.cratis/me first. It reads the cookie only when that endpoint answers 404, for example behind a proxy that does not route it to the application yet. When it does, it logs a warning in the browser console. That fallback will be removed in a future major version.

  • Code that read .cratis-identity from document.cookie must call IdentityProvider.getCurrent(), or useIdentity() in React, instead.
  • Replace IdentityProvider.clearIdentityCookie() with IdentityProvider.clearCache().
  • Identity is no longer available before the first request completes: render a loading state until getCurrent() resolves. In React, useIdentity() reports isLoading for this.

See migrating from the identity cookie for the full picture, including mixed frontend and backend versions.