@ohlom/login-react-native
v0.1.0
Published
Login with Ohlom for React Native — PKCE Authorization Code via the native auth browser (react-native-app-auth). signIn / refresh / getUser / signOut.
Maintainers
Readme
@ohlom/login-react-native
Login with Ohlom for React Native — Authorization Code + PKCE (S256) via
the native OS auth browser, powered by
react-native-app-auth.
This is a public client: no client secret ships in the app; PKCE protects the code exchange. Mirrors the Ohlom Flutter plugin.
Contract
| | |
|---|---|
| Base URL | https://api.ohlom.com |
| Authorize | GET /oauth/authorize (response_type=code, PKCE S256) |
| Token | POST /oauth/token (form-encoded, no secret — public client) |
| Userinfo | GET /oauth/userinfo (Authorization: Bearer → sub, name, phone_number) |
| Discovery | /.well-known/openid-configuration |
| Scopes | openid profile phone (+ partner scopes) |
Ohlom keys accounts on sub and returns no email.
Install
Bare React Native
npm install @ohlom/login-react-native react-native-app-auth
cd ios && pod install # iOSExpo
react-native-app-auth needs native config, so use a dev/prebuild client
(it does not work in Expo Go):
npx expo install react-native-app-auth
npm install @ohlom/login-react-native
npx expo prebuildAdd the config plugin in app.json:
{ "expo": { "plugins": ["react-native-app-auth"] } }Prefer a pure managed Expo flow with no native config? Use
expo-auth-sessioninstead — see Expo variant below.
Usage
import { createOhlomAuth } from "@ohlom/login-react-native";
const ohlom = createOhlomAuth({
clientId: "ohlom_abc123",
redirectUrl: "la.ohlom.example://oauthredirect",
scopes: ["openid", "profile", "phone"], // optional; this is the default
});
// Opens the native browser, runs PKCE, returns tokens + user.
const { tokens, user } = await ohlom.signIn();
// user => { sub, name?, phone_number? } (no email)
// PERSIST tokens securely yourself — see "Token storage" below.
// Later, refresh:
const fresh = await ohlom.refresh(tokens.refreshToken!);
// Fetch userinfo for an access token:
const u = await ohlom.getUser(fresh.accessToken);
// Sign out: drop tokens from your secure store. (Ohlom has no remote revocation.)
await ohlom.signOut();API
| Method | Description |
|---|---|
| createOhlomAuth({ clientId, redirectUrl, scopes?, baseUrl?, useDiscovery?, iosPrefersEphemeralSession? }) | Build the auth handle. |
| signIn(): Promise<{ tokens, user }> | Native browser → PKCE flow → userinfo. Rejects if the user cancels. |
| refresh(refreshToken): Promise<OhlomTokens> | refresh_token grant. |
| getUser(accessToken): Promise<OhlomUser> | GET /oauth/userinfo. |
| signOut({ tokenToRevoke? }?): Promise<void> | No-op remotely (drop tokens locally); best-effort revoke if ever supported. |
useDiscovery: true uses issuer + the well-known doc; default false uses the
explicit /oauth/authorize + /oauth/token endpoints.
Token storage (do this yourself)
This library never persists or logs tokens. Store them in the secure enclave:
Bare RN — react-native-keychain
import * as Keychain from "react-native-keychain";
await Keychain.setGenericPassword("ohlom", JSON.stringify(tokens), {
service: "com.example.ohlom.tokens",
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
const creds = await Keychain.getGenericPassword({ service: "com.example.ohlom.tokens" });
const tokens = creds ? JSON.parse(creds.password) : null;Expo — expo-secure-store
import * as SecureStore from "expo-secure-store";
await SecureStore.setItemAsync("ohlom.tokens", JSON.stringify(tokens));
const tokens = JSON.parse((await SecureStore.getItemAsync("ohlom.tokens")) ?? "null");Native setup (redirect URI)
Register your redirectUrl (custom scheme or universal/app link) with Ohlom at
dev.ohlom.com, and wire the OS to route it back to the app.
iOS — URL scheme
ios/<App>/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>la.ohlom.example</string>
</array>
</dict>
</array>ios/<App>/AppDelegate.mm (or .swift) — forward the callback to RNAppAuth.
See the react-native-app-auth iOS setup.
Android — intent filter
android/app/src/main/AndroidManifest.xml — add to your main activity:
<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="la.ohlom.example" android:host="oauthredirect" />
</intent-filter>And in android/app/build.gradle:
android {
defaultConfig {
manifestPlaceholders = [appAuthRedirectScheme: 'la.ohlom.example']
}
}Expo variant (expo-auth-session)
For a fully managed Expo flow (no native config / works without prebuild), use
expo-auth-session with the same Ohlom endpoints and PKCE:
import * as AuthSession from "expo-auth-session";
const redirectUri = AuthSession.makeRedirectUri({ scheme: "la.ohlom.example" });
const discovery = {
authorizationEndpoint: "https://api.ohlom.com/oauth/authorize",
tokenEndpoint: "https://api.ohlom.com/oauth/token",
userInfoEndpoint: "https://api.ohlom.com/oauth/userinfo",
};
const request = new AuthSession.AuthRequest({
clientId: "ohlom_abc123",
redirectUri,
scopes: ["openid", "profile", "phone"],
usePKCE: true, // S256
});
const result = await request.promptAsync(discovery);
if (result.type === "success") {
const token = await AuthSession.exchangeCodeAsync(
{
clientId: "ohlom_abc123",
code: result.params.code,
redirectUri,
extraParams: { code_verifier: request.codeVerifier! },
},
discovery,
);
// token.accessToken … then GET /oauth/userinfo with a Bearer header.
}Store tokens with expo-secure-store as shown above.
Notes
- Native testing requires a real device or simulator/emulator — the auth browser cannot run in CI/headless.
- This library never logs tokens.
- Ohlom has no end-session/revocation endpoint; signing out is local.
License
MIT
