Authdog

Field companion

Last updated Oct 7, 2026
View as Markdown

The field companion lists today's site visits after hosted sign-in returns through a deep link. The session token stays in secure storage.

Clone starters/expo. SDK reference: React Native / Expo.

The job list answers who the technician is on the device. Client state is UX, not a security boundary. Your API must validate the bearer. Apply authorization there.

What you need

Requirement Detail
Public key pk_... from the console
Environment Copy .env.example to .env and set EXPO_PUBLIC_PK_AUTHDOG
Secret key Never put sk_... in the app
Callback Register the deep-link callback. Local default: authdog-authn://callback

Run

npm install
npx expo start

Open the project in Expo Go or a simulator. Tap Sign in, finish the hosted Account portal, and return through the deep link. You should see job_18 (Pier 4, replace meter) and job_19 (Warehouse B, calibrate sensor).

Jobs

handleRedirect stores ?token= after a structure check only. fetchUser() validates it through userinfo. The visits render only after that call. This example uses expo-secure-store. Do not ship the in-memory fallback.

// App.tsx
import { useEffect } from "react"
import { Button, Linking, Text } from "react-native"
import * as SecureStore from "expo-secure-store"
import {
  AuthdogProvider,
  createSecureStoreAdapter,
  useRedirectHandler,
  useSignIn,
  useUser,
} from "@authdog/react-native"

const publicKey = process.env.EXPO_PUBLIC_PK_AUTHDOG ?? ""

function Home() {
  const { signIn } = useSignIn()
  const { handleRedirect } = useRedirectHandler()
  const { fetchUser, isAuthenticated } = useUser()
  const redirectUrl = "authdog-authn://callback"

  useEffect(() => {
    Linking.getInitialURL().then((url) => {
      if (url) handleRedirect(url)
    })
    const sub = Linking.addEventListener("url", ({ url }) => handleRedirect(url))
    return () => sub.remove()
  }, [handleRedirect])

  useEffect(() => {
    if (isAuthenticated) fetchUser()
  }, [isAuthenticated, fetchUser])

  if (!isAuthenticated) {
    return <Button title="Sign in" onPress={() => signIn(redirectUrl)} />
  }

  return (
    <>
      <Text>job_18 · Pier 4 · Replace meter · 08:00–10:00</Text>
      <Text>job_19 · Warehouse B · Calibrate sensor · 11:30–12:00</Text>
    </>
  )
}

export default function App() {
  return (
    <AuthdogProvider
      publicKey={publicKey}
      storage={createSecureStoreAdapter(SecureStore)}
    >
      <Home />
    </AuthdogProvider>
  )
}