starti.app
Getting started

Adding Authentication

Sign users in with Google, Apple, Microsoft, or MitID and manage authentication sessions

Adding Authentication

By default, username/password login in your app works exactly the same way as it does on your website — no extra SDK work is needed. If you want to add biometric login (Face ID / fingerprint), see Biometric Login.

This page covers social and third-party sign-in — Google, Apple, Microsoft, MitID and more. We currently support the providers listed below, and we continuously add new ones as the need arises.

Supported providers

The SDK supports the following built-in providers:

ProviderproviderName value
Google"google"
Apple"apple"
Microsoft"microsoft"
MitID (Denmark)"signaturgruppenmitid"

You can also pass any custom provider name as a string if you have configured one in the manager.

Sign in with Google

The signIn() method opens the native authentication flow for the requested provider. It returns a Promise that resolves with the authentication result.

const result = await startiapp.Auth.signIn("google");

That single line opens a native Google sign-in dialog. The user authenticates with their Google account, and the SDK returns the result to your code.

Handle the authentication result

Every result object has an isSuccess boolean. When the sign-in succeeds, you get an authorization code and related fields that your backend can exchange for tokens.

const result = await startiapp.Auth.signIn("google");

if (result.isSuccess) {
  console.log("Authorization code:", result.authorizationCode);
  console.log("Code verifier:", result.codeVerifier);
  console.log("Redirect URI:", result.redirectUri);

  // Send these to your backend to exchange for access/refresh tokens
  await sendToBackend({
    authorizationCode: result.authorizationCode,
    codeVerifier: result.codeVerifier,
    redirectUri: result.redirectUri,
  });
} else {
  console.log("Sign-in failed:", result.errorMessage);
}

The result for a successful sign-in (for Google, Microsoft, MitID) contains:

PropertyTypeDescription
isSuccesstrueIndicates success
providerNamestringThe provider that was used
authorizationCodestringOAuth authorization code to exchange on your server
codeVerifierstringPKCE code verifier for the token exchange
redirectUristringThe redirect URI used during the flow
additionalClaimsRecord<string, string>Extra claims returned by the provider

A failed result contains:

PropertyTypeDescription
isSuccessfalseIndicates failure
providerNamestringThe provider that was used
errorMessagestringA human-readable error description

The authorizationCode is a one-time-use code. Exchange it on your server immediately and do not store it on the client.

Check for an existing session

Use getCurrentSession() to see if the user already has an active session. This is useful on page load to skip the sign-in screen.

const session = await startiapp.Auth.getCurrentSession();

if (session) {
  console.log("User is already signed in.");
  console.log("Provider:", session.providerName);
} else {
  console.log("No active session -- show sign-in screen.");
}

There is also a convenience method isAuthenticated() that returns a simple boolean.

const loggedIn = await startiapp.Auth.isAuthenticated();

if (loggedIn) {
  // proceed to the app
} else {
  // show the login page
}

Both getCurrentSession() and isAuthenticated() are asynchronous because they query the native bridge.

Sign out

Call signOut() to end the user's session.

const success = await startiapp.Auth.signOut();

if (success) {
  console.log("User has been signed out.");
  // Redirect to the login page
} else {
  console.log("Sign-out failed.");
}

Apple sign-in specifics

Apple Sign In returns a different result shape compared to other providers. Instead of authorizationCode + codeVerifier, Apple returns an identity object with user details.

const result = await startiapp.Auth.signIn("apple");

if (result.isSuccess) {
  console.log("User ID:", result.identity.userId);
  console.log("Email:", result.identity.email);
  console.log("Name:", result.identity.name);
  console.log("ID Token:", result.identity.idToken);
  console.log("Authorization code:", result.authorizationCode);
  console.log("Redirect URI:", result.redirectUri);
}

The Apple result contains:

PropertyTypeDescription
identity.userIdstringApple's stable user identifier
identity.emailstringThe user's email address
identity.namestring | nullThe user's name (only on first sign-in)
identity.idTokenstringA signed JWT from Apple
authorizationCodestringOAuth authorization code
redirectUristringThe redirect URI used during the flow

Apple only provides the user's name on the very first sign-in. On subsequent sign-ins, identity.name is null. Make sure your backend stores the name during the first authentication.

MitID sign-in with options

MitID (via Signaturgruppen) supports additional options. You can require a CPR number (Danish social security number) or specify custom scopes.

// Request CPR number
const result = await startiapp.Auth.signIn("signaturgruppenmitid", {
  scope: "openid mitid ssn",
});

if (result.isSuccess) {
  console.log("CPR:", result.additionalClaims["cpr"]);
}

MitID test environment

During development, MitID uses the pre-production environment (pp.netseidbroker.dk). You can create test identities at pp.mitid.dk/test-tool/frontend/#/create-identity.

Listening for authentication events

The Auth integration dispatches an authenticationCompleted event when sign-in finishes. This can be useful if you want a centralized authentication handler.

startiapp.Auth.addEventListener("authenticationCompleted", function (event) {
  const result = event.detail;

  if (result.isSuccess) {
    console.log("Authenticated with:", result.providerName);
  }
});

Complete working example

Here is a full example combining all the steps into a simple authentication flow.

async function main() {
  if (!startiapp.isRunningInApp()) {
    console.log("Authentication requires the native app.");
    return;
  }

  await startiapp.initialize();

  // Check for existing session
  const existingSession = await startiapp.Auth.getCurrentSession();

  if (existingSession) {
    console.log("Welcome back! Provider:", existingSession.providerName);
    showApp();
    return;
  }

  // No session -- sign in
  console.log("No session found. Starting sign-in...");
  const result = await startiapp.Auth.signIn("google");

  if (result.isSuccess) {
    console.log("Signed in successfully!");
    console.log("Authorization code:", result.authorizationCode);

    // Exchange the authorization code on your backend
    await fetch("/api/auth/exchange", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        authorizationCode: result.authorizationCode,
        codeVerifier: result.codeVerifier,
        redirectUri: result.redirectUri,
        provider: result.providerName,
      }),
    });

    showApp();
  } else {
    console.error("Sign-in failed:", result.errorMessage);
    showError(result.errorMessage);
  }
}

function showApp() {
  document.getElementById("login-screen").style.display = "none";
  document.getElementById("app-content").style.display = "block";
}

function showError(message) {
  document.getElementById("error-text").textContent = message;
}

// Wire up the sign-out button
document.getElementById("sign-out-btn").addEventListener("click", async function () {
  const success = await startiapp.Auth.signOut();
  if (success) {
    window.location.reload();
  }
});

main();

On this page