signin-mobile
v0.1.0
Published
Brokered social login for React Native — native system auth session (ASWebAuthenticationSession / Custom Tabs). Companion to HIKO Social Login Plus.
Maintainers
Readme
signin-mobile
Native sign-in for React Native, powered by HIKO Social Login Plus on the Shopify App Store.
signin-mobile is the official React Native companion to HIKO Social Login
Plus — the Shopify app that adds social, passwordless, and passkey sign-in to a
Shopify storefront. This package brings those exact sign-in methods, and the
merchant's exact configuration, into a native iOS/Android app.
Your app renders native buttons; the actual OAuth/OTP step runs in the
platform's system authentication session (ASWebAuthenticationSession on
iOS, Chrome Custom Tabs on Android). There is no embedded WebView, no bundled
Facebook/Google SDK, and no client secret in the app — the HIKO broker is an
OIDC broker, so the app only ever handles an opaque session token.
- ✅ Google, Facebook, Apple, LINE, and 20+ social providers
- ✅ Email one-time-code, WhatsApp, and passkey (passwordless)
- ✅ Buttons styled from the merchant's HIKO dashboard (theme, shape, brand icons)
- ✅ Marketing/terms consent gate honored automatically
- ✅ Opaque session persisted in the Keychain / Keystore (
expo-secure-store) - ✅ Customer Account API GraphQL queries through the broker
Table of contents
- How it works
- Requirements
- Installation
- Deep-link (URL scheme) setup
- Quick start
- Configuration
- Server-driven appearance & methods
- Custom UI with
useHikoAuth - Querying customer data
- API reference
- Troubleshooting
- License
How it works
HIKO Social Login Plus (the Shopify app, "the broker") already runs a per-shop
OIDC login flow for the storefront. signin-mobile reuses that same broker and
the same providers — it only changes the transport of the login step.
┌─────────────────────────┐
│ Your React Native app │
│ ┌───────────────────┐ │ 1. GET /headless/config?shop=…
│ │ <HikoSignin/> │──┼──────────────────────────────────► HIKO broker
│ │ native buttons, │ │ providers, styles, passwordless, consent
│ │ styled from cfg │◄─┼──────────────────────────────────
│ └─────────┬─────────┘ │
│ │ tap │
│ ▼ │ 2. openAuthSessionAsync(/headless/start?…)
│ ┌───────────────────┐ │──────────────────────────────────► broker → Shopify
│ │ ASWebAuthSession /│ │ Customer Accounts → provider / OTP / passkey
│ │ Chrome Custom Tabs│◄─┼──────────────────────────────────
│ └─────────┬─────────┘ │ 3. redirect → myapp://callback#hiko_session=…
│ ▼ │
│ ┌───────────────────┐ │ 4. token stored in Keychain / Keystore
│ │ expo-secure-store│ │ (opaque; no secrets in the app)
│ └───────────────────┘ │
└─────────────────────────┘ 5. query customer via POST /headless/customer- Config — the widget fetches
/headless/configfor the shop and renders the enabled providers with the merchant's styling (brand icons, theme, shape, sizes) plus any passwordless methods and consent gate. - Login — tapping a button opens the system auth session at
/headless/start. The broker walks the user through Shopify Customer Accounts and the chosen method entirely in the trusted system browser. - Return — on success the broker redirects to your app's custom scheme with
an opaque
hiko_sessionin the URL fragment. The system session hands that URL back to the app. - Persist — the token is saved in
expo-secure-store(iOS Keychain / Android Keystore) and restored on next launch. - Query — authenticated Customer Account API GraphQL requests are proxied through the broker with the session as a bearer token.
Because the whole OAuth/OTP exchange happens in the system browser against the broker's origin, passkeys work with no native WebAuthn integration, and there is never a provider secret or access token inside your app.
Requirements
- React Native 0.7x+ (Expo SDK 50+ recommended; built and tested on Expo SDK 56).
- The shop must have HIKO Social Login Plus installed and configured (providers enabled, and your app's redirect allowlisted — see Configuration).
- Peer dependencies (installed alongside this package):
expo-web-browser— the system auth sessionexpo-secure-store— encrypted session storagereact-native-svg— brand icon renderingreact,react-native
The package uses Expo modules but does not require an Expo-managed app — it runs in bare React Native too via
install-expo-modules(see below).
Installation
npm install signin-mobileThen install the peer dependencies. In an Expo project:
npx expo install expo-web-browser expo-secure-store react-native-svgIn a bare React Native project:
npm install expo-web-browser expo-secure-store react-native-svg
# one-time: enable Expo modules in a non-Expo RN app
npx install-expo-modules@latest
npx pod-install # iOSDeep-link (URL scheme) setup
Login returns to your app via a custom URL scheme (e.g. myapp://callback).
The scheme you pass as appRedirect must be registered by the app and
allowlisted on the broker for the shop.
Expo (managed / dev client)
In app.json / app.config.js, set the app scheme and add the Expo module
config plugins:
{
"expo": {
"scheme": "myapp",
"plugins": ["expo-web-browser", "expo-secure-store"]
}
}Build a dev client or standalone build (npx expo run:ios /
npx expo run:android). The custom-scheme redirect does not work in Expo Go,
which owns the exp:// scheme.
Bare React Native — iOS
Register the scheme in ios/<App>/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>ASWebAuthenticationSession captures the callback scheme for the auth session, so
no extra AppDelegate wiring is required for login itself. Run npx pod-install
after installing the peers.
Bare React Native — Android
Add an intent-filter for the scheme to your main activity in
android/app/src/main/AndroidManifest.xml:
<activity android:name=".MainActivity" android:launchMode="singleTask" ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
</activity>Chrome Custom Tabs returns to the app through this filter.
Quick start
import { HikoSignin } from "signin-mobile";
export default function SignInScreen() {
return (
<HikoSignin
shop="your-shop.myshopify.com"
configServer="https://signin.hiko.software"
appRedirect="myapp://callback"
onLogin={(customer) => console.log("Signed in as", customer?.firstName)}
onLogout={() => console.log("Signed out")}
/>
);
}HikoSignin handles the whole lifecycle: it loads the shop config, renders the
styled provider buttons + passwordless entries, runs the auth session, persists
the session, restores it on next launch, and exposes signed-in / signed-out
callbacks.
Configuration
| Prop | Type | Description |
| -------------- | -------------------- | ---------------------------------------------------------------------- |
| shop | string (required) | The *.myshopify.com domain with HIKO Social Login Plus installed. |
| configServer | string | Broker base URL. Defaults to https://signin.hiko.software. |
| appRedirect | string (required) | Your app's custom-scheme return URL, e.g. myapp://callback. |
| onLogin | (customer) => void | Fires once when a customer is signed in (with fetched customer data). |
| onLogout | () => void | Fires on every signed-in → signed-out transition. |
Allowlist your redirect.
appRedirectmust be added to the shop's headless Allowed origins in the HIKO Social Login Plus admin. If it isn't, the broker rejects the login withinvalid_return(scheme not accepted) orreturn_not_allowed(not in the allowlist).
Server-driven appearance & methods
Everything the widget shows comes from /headless/config, so merchants control it
from the HIKO dashboard with no app release:
styles— theme (color/dark/light), shape (pill/rounded/square), format (icontext/center/right/text/icon), sizes, colors, and per-button overrides. Brand icons are rendered from vendored SVG marks viareact-native-svg.providers— the ordered list of enabled social providers.passwordless—{ otp, whatsapp, passkey }. Whenotpis on the widget shows an email field;whatsappadds a WhatsApp button; passkey is offered on the broker's passwordless page during the email flow.consent—{ enabled, defaultChecked }. When enabled, a terms-consent checkbox is shown and sign-in is blocked until it's ticked.
Custom UI with useHikoAuth
Prefer to build your own UI? Use the hook and skip <HikoSignin> entirely.
import { useHikoAuth } from "signin-mobile";
import { Button, Text, View } from "react-native";
export function MySignIn() {
const {
isLoggedIn,
customer,
login, // (provider) => Promise<boolean>
loginWithEmail, // (email) => Promise<boolean>
loginWithWhatsapp,
logout,
query,
} = useHikoAuth({
shop: "your-shop.myshopify.com",
configServer: "https://signin.hiko.software",
appRedirect: "myapp://callback",
});
if (isLoggedIn) {
return (
<View>
<Text>Hi {customer?.firstName ?? "there"}</Text>
<Button title="Log out" onPress={logout} />
</View>
);
}
return (
<View>
<Button title="Continue with Google" onPress={() => login("google")} />
<Button title="Email me a code" onPress={() => loginWithEmail("[email protected]")} />
<Button title="WhatsApp" onPress={loginWithWhatsapp} />
</View>
);
}Querying customer data
Once signed in, run Customer Account API GraphQL through the broker:
const { auth } = useHikoAuth({ /* … */ });
const data = await auth.query(`
{
customer {
firstName
lastName
emailAddress { emailAddress }
orders(first: 5) { edges { node { name } } }
}
}
`);auth.query attaches the persisted session as a bearer token and calls
/headless/customer. A 401 clears the session and signs the user out.
API reference
Exports
import {
HikoSignin, // <HikoSignin /> drop-in component
useHikoAuth, // hook: state + login/logout for custom UIs
createMobileAuth, // low-level auth client (no React)
loadConfig, // fetch /headless/config
createAuthSession, // wrap expo-web-browser (preferEphemeralSession by default)
parseSessionFromUrl, // extract hiko_session from a return URL
createStorage, // expo-secure-store token storage
} from "signin-mobile";useHikoAuth({ shop, configServer, appRedirect })
Returns { auth, isLoggedIn, customer, login, loginWithEmail, loginWithWhatsapp,
logout, query, getToken }.
createMobileAuth({ shop, configServer?, appRedirect, fetchImpl?, authSession?, storage? })
The headless client behind the hook. Methods:
| Method | Description |
| -------------------------- | --------------------------------------------------------------- |
| loadConfig() | Fetch the per-shop widget config. |
| restore() | Rehydrate a persisted session on app launch. |
| login(provider) | Social sign-in via the system auth session. Resolves boolean.|
| loginWithEmail(email) | Email one-time-code (and passkey) sign-in. |
| loginWithWhatsapp() | WhatsApp one-time-code sign-in. |
| query(query, variables?) | Customer Account API GraphQL through the broker. |
| getToken() | The current Customer Account API token info. |
| session() | Current session info from the broker. |
| logout() | Revoke on the broker and clear local storage. |
| isLoggedIn() | Boolean, in-memory. |
| getLastCustomer() | The last customer object fetched, if any. |
| onChange(cb) | Subscribe to auth-state changes. Returns an unsubscribe fn. |
| onLoginPhase(cb) | Observe start / cancel login phases. |
Config shape (/headless/config)
{
entitled: boolean,
providers: string[],
styles: { theme, shape, format, buttonSize, iconSize, divider, /* + overrides */ },
passwordless: { otp: boolean, whatsapp: boolean, passkey: boolean },
consent: { enabled: boolean, defaultChecked: boolean },
widgetText: Record<string, unknown>,
storefront: Record<string, unknown>
}Troubleshooting
| Symptom | Cause & fix |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| invalid_return | The broker doesn't accept your redirect scheme. Ensure the broker supports custom-scheme returns and your appRedirect is allowlisted. |
| return_not_allowed | appRedirect is a valid format but not in the shop's headless Allowed origins. Add it in the HIKO admin. |
| Login opens then returns to a blank app | The custom scheme isn't registered (Info.plist / AndroidManifest / Expo scheme), or you're running in Expo Go. Use a dev/standalone build. |
| "…Wants to Use … to Sign In" iOS prompt | The auth session is non-ephemeral. createAuthSession defaults to preferEphemeralSession: true; pass false to opt back into shared-Safari SSO. |
| No buttons render | The shop isn't entitled or has no providers enabled in HIKO Social Login Plus. |
License
MIT
