Browser

Browser wraps expo-web-browser: a page shown in an in-app browser, and a sign-in flow through the system's own authentication session.

Import
import { Browser } from '@ng-native/expo';
Inject
inject(Browser)

Install

npx expo install expo-web-browser
import { Browser } from '@ng-native/expo/browser';

The smallest thing that works

import { Component, inject } from '@angular/core';
import { Browser } from '@ng-native/expo/browser';

@Component({
  selector: 'app-sign-in',
  template: '<pressable (press)="signIn()"><text>Sign in</text></pressable>',
})
export class SignIn {
  private readonly browser = inject(Browser);

  protected async signIn(): Promise<void> {
    const returned = await this.browser.signIn(
      'https://id.example.com/authorize?client_id=...&redirect_uri=myapp://signed-in',
      'myapp://signed-in',
    );
    if (returned) {
      const code = new URL(returned).searchParams.get('code');
    }
  }
}

Opening a page and signing in

  • open(url) shows any page in the in-app browser - SFSafariViewController on iOS, a Custom Tab on Android - and resolves when the person closes it. Use it for anything that is not a sign-in: a privacy policy, a receipt, an external link.
  • signIn(url, redirectUrl) opens the system's authentication session instead - ASWebAuthenticationSession on iOS, a Custom Tab on Android - which shares the browser's cookies (so an existing session is picked up) and closes itself when the page redirects to redirectUrl. It resolves to that URL, with its code or token in the query string, ready for the app to exchange - or to null if the person closed it first.

As of iOS 11, SFSafariViewController no longer shares cookies with Safari - signIn()'s ASWebAuthenticationSession does, which is why sign-in uses it and open() does not.

The full OAuth flow, with PKCE

signIn() covers a redirect-and-read-the-URL flow. For the full flow - PKCE, a provider's discovery document - expo-auth-session's AuthRequest is a plain class that needs no React, and its promptAsync opens this same session:

import { AuthRequest, makeRedirectUri, exchangeCodeAsync } from 'expo-auth-session';

const discovery = { authorizationEndpoint: '...', tokenEndpoint: '...' };
const request = new AuthRequest({
  clientId: 'my-client',
  scopes: ['openid', 'profile'],
  redirectUri: makeRedirectUri({ scheme: 'myapp' }),
});
const result = await request.promptAsync(discovery);
if (result.type === 'success') {
  const tokens = await exchangeCodeAsync(
    {
      clientId: 'my-client',
      code: result.params['code']!,
      redirectUri: request.redirectUri,
      extraParams: { code_verifier: request.codeVerifier! },
    },
    discovery,
  );
}

expo-auth-session is a separate package (npx expo install expo-auth-session); Browser does not depend on it.

Without the module

open() and signIn() resolve without opening anything - signIn() resolves to null, as though the person had closed the page immediately.

Reference

Browserservice@ng-native/expo
import { Browser } from '@ng-native/expo';

Methods

open(url: string): Promise<void>

Open a page in the in-app browser. Resolves when it is closed.

signIn(url: string, redirectUrl: string): Promise<string | null>

Open a sign-in page, and resolve to the URL it redirected back to - or null if they closed it first.

Angular components as native iOS and Android views, built and shipped with Expo.

An alpha. MIT licensed. Sponsor its development.

An independent project, not affiliated with or endorsed by Google, the Angular team or Expo. Angular is a trademark of Google LLC.