Minimal Expo-compatible Swig IdP SDK scaffold.
SwigIdpProvideruseSwigIdp()createSwigWebClient()from@swig-wallet/expo-idp-sdk/web- public types from
provider.tsx
src/
hooks/
use-swig-idp.ts
proof/
proof-pipeline.ts
states/
states.ts
bootstrap.ts
begin-auth.ts
complete-auth.ts
start-oauth.ts
start-email-otp.ts
oauth-callback.ts
swig-session/
session-service.ts
session-store.ts
transport/
api.ts
web.ts
config.ts
index.ts
provider.tsximport { SwigIdpProvider, useSwigIdp } from "@swig-wallet/expo-idp-sdk";
function LoginButton() {
const { startOAuth, isAuthenticated, authPhase } = useSwigIdp();
if (isAuthenticated) {
return null;
}
return (
<button
onClick={async () => {
await startOAuth({
provider: "google",
clientId: "your-client-id",
policyId: "your-policy-id",
flow: "role",
});
}}
>
{authPhase === "begin_oauth" ? "Opening browser..." : "Continue"}
</button>
);
}
export function App() {
return (
<SwigIdpProvider
config={{
redirectUri: "yourapp://auth/callback",
}}
>
<LoginButton />
</SwigIdpProvider>
);
}function EmailLogin() {
const { startEmailOtp, authPhase } = useSwigIdp();
return (
<button
onClick={async () => {
await startEmailOtp({
// provider defaults to "email_otp"; pass an explicit value if a
// tenant has registered its own email-otp provider key.
clientId: "your-client-id",
policyId: "your-policy-id",
flow: "role",
});
}}
>
{authPhase === "begin_oauth" ? "Opening browser..." : "Continue with email"}
</button>
);
}startEmailOtp opens the isolated host's email entry page in a system auth session — the user types their address inside the IH, the IH sends the code, and the same OTP entry + verify + callback flow runs from there. The address, the verification code, and the callback JWT never enter the developer app. The function resolves with the same session payload OAuth produces.
function SmsLogin() {
const { startSmsOtp, authPhase } = useSwigIdp();
return (
<button
onClick={async () => {
await startSmsOtp({
// provider defaults to "sms"; pass an explicit value if a tenant
// has registered its own sms-otp provider key.
clientId: "your-client-id",
policyId: "your-policy-id",
flow: "role",
});
}}
>
{authPhase === "begin_oauth" ? "Opening browser..." : "Continue with phone"}
</button>
);
}startSmsOtp opens the isolated host's phone entry page in a system auth session — the user types their phone number inside the IH, the IH sends the SMS code, and the same OTP entry + verify + callback flow runs from there. The phone number, verification code, and callback JWT never enter the developer app. The function resolves with the same session payload OAuth produces.
Use the /web entry point in browser apps. This path does not import Expo modules.
import { Network, createSwigWebClient } from "@swig-wallet/expo-idp-sdk/web";
const swig = createSwigWebClient({
redirectUri: `${window.location.origin}/auth/callback`,
network: Network.Devnet,
});
await swig.redirectToOAuth({
provider: "google",
clientId: "your-client-id",
policyId: "your-policy-id",
flow: "role",
});In your callback route:
import { createSwigWebClient } from "@swig-wallet/expo-idp-sdk/web";
const swig = createSwigWebClient();
const session = await swig.completeOAuthFromUrl(window.location.href);
console.log(session.configAddress, session.walletAddress, session.roleId);Useful web client methods:
getOAuthStartUrl(input)builds the isolated host URL without navigating.redirectToOAuth(input)performs a full-page browser redirect.completeOAuthFromUrl(url)parses and persists the callback session.createSigner(input)signs prepared Solana transactions in a visible isolated-host approval window.getSession()returns persisted session data.getPersistedSession()returns the full persisted session.logout()clears the stored session.listProviders({ clientId })lists configured IdP providers.
Web session persistence defaults to window.localStorage. Pass a custom storage adapter to use sessionStorage, cookies, or framework-managed storage.
Managed-session callbacks include the isolated host's Ed25519 public key and a
bounded expiration. The corresponding non-exportable private key remains on the
isolated-host origin. createSigner() requires that managed-session metadata,
opens the isolated host for explicit approval, and returns the transaction only
after the isolated host signs it.
proof/*,swig-session/*,states/*, andtransport/*are internal modules and not part of the stable SDK contract.- Expo session persistence defaults to
expo-secure-store. Pass a customstorageadapter in config to override. baseUrlis optional and defaults tohttps://backend.prod.infra.onswig.com.isolatedHostUrlis optional and defaults tohttps://swig-dev-portal-isolated-host.vercel.app.- Install
expo-secure-storein the host Expo/React Native app. - High-level public flows are
startOAuth(),startEmailOtp(), andstartSmsOtp(). All three return the samePersistedSwigSessionshape. transport/api.tsis a thin 1:1 wrapper overapi_idp.protoandapi_wallet.protoHTTP mappings.- OAuth start now prefers a backend-issued
start_tokenfor the isolated host redirect, and falls back to the legacy raw redirect params only when the backend has not been upgraded yet. - Mobile auth is intentionally routed through
expo-web-browsersystem auth sessions viaopenAuthSessionAsync. - Embedded
WebViewauth is unsupported. Do not load the isolated host insidereact-native-webviewor any host-controlled in-app browser if you need production security guarantees. - Business logic remains scaffolded with TODOs in:
proof/proof-pipeline.tsswig-session/session-service.tsstates/complete-auth.ts
completeAuth()accepts the signup shape (client_id,network,zk_proof).- Proof refreshes return transient
ProgramExecProofrequester authority metadata and do not create session-key roles. - Auth phases:
startbegin_oauthcomplete_oauthbegin_swig_jwtend_swig_jwtbegin_proofend_proofbegin_session_exchangeswig_program_session_startedauthenticatederror
- TODO areas:
- proof generation
- session exchange
- session lifecycle policy
- Default endpoint mapping:
listProviders->/identity/api/providersstartAuth->/identity/api/auth/startstartEmailOtp->/identity/api/auth/email/startstartSmsOtp->/identity/api/auth/sms/startsignup->/identity/api/signuplookupSwig->/wallet/swig/lookupgetSwigStatus->/wallet/swig/statuscheckSwigAuth->/wallet/swig/auth/checkcreateSwigSession->/wallet/swig/sessiongetPolicy->/wallet/policies/{policy_id}
- The SDK's supported mobile auth paths are
startOAuth(),startEmailOtp(), andstartSmsOtp(). All open the isolated host in a system auth session. - Do not embed the isolated host in a
WebView. A host app that owns theWebViewcan inspect DOM, URLs, and storage. The OTP code, callback JWT, and the user's email or phone number are all held inside the isolated host on purpose. - If you need a custom mobile integration, preserve the same boundary: backend start token -> isolated host -> system auth session -> deep link callback.
Apache-2.0