
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
@forward-software/react-auth-apple
Advanced tools
Apple Sign-In adapter for @forward-software/react-auth - Web and React Native/Expo
Apple Sign-In adapter for @forward-software/react-auth - Web and React Native
Self-contained Apple Sign-In integration with no external auth wrapper dependencies. Provides a ready-made AuthClient implementation and a drop-in AppleSignInButton for both platforms.
npm install @forward-software/react-auth-apple @forward-software/react-auth
# or
pnpm add @forward-software/react-auth-apple @forward-software/react-auth
This package includes an Expo native module. You need a development build (not Expo Go):
npx expo prebuild
npx expo run:ios
No additional CocoaPods are required -- Apple Sign-In uses the system AuthenticationServices framework.
import { createAuth } from '@forward-software/react-auth';
import { AppleAuthClient, AppleSignInButton } from '@forward-software/react-auth-apple';
const appleClient = new AppleAuthClient({
clientId: 'com.example.service', // Your Apple Services ID
redirectURI: 'https://example.com/auth/apple/callback',
});
const { AuthProvider, useAuthClient } = createAuth(appleClient);
function App() {
return (
<AuthProvider>
<LoginScreen />
</AuthProvider>
);
}
function LoginScreen() {
const auth = useAuthClient();
return (
<AppleSignInButton
config={{
clientId: 'com.example.service',
redirectURI: 'https://example.com/auth/apple/callback',
}}
onCredential={(credentials) => auth.login(credentials)}
onError={(error) => console.error(error)}
/>
);
}
import { createAuth } from '@forward-software/react-auth';
import { AppleAuthClient, AppleSignInButton } from '@forward-software/react-auth-apple';
import { MMKV } from 'react-native-mmkv';
const mmkv = new MMKV();
const storage = {
getItem: (key: string) => mmkv.getString(key) ?? null,
setItem: (key: string, value: string) => mmkv.set(key, value),
removeItem: (key: string) => mmkv.delete(key),
};
const appleClient = new AppleAuthClient({
clientId: 'com.example.app',
storage,
});
const { AuthProvider, useAuthClient } = createAuth(appleClient);
function LoginScreen() {
const auth = useAuthClient();
return (
<AppleSignInButton
config={{ clientId: 'com.example.app', storage }}
onCredential={(credentials) => auth.login(credentials)}
onError={(error) => console.error(error)}
/>
);
}
clientId and your registered redirect URL as redirectURINo additional setup is needed beyond enabling the Sign in with Apple capability in your Xcode project:
Apple Sign-In on Android uses a web-based OAuth flow via Chrome Custom Tabs. Because Apple uses response_mode=form_post, a backend proxy is required to receive Apple's POST response and redirect it back to your app via a deep link.
id_token and code to your registered redirect URI (your backend)handleCallback(), and the sign-in promise resolvesYou need a Services ID (separate from your App ID):
com.example.app.service)api.example.com)https://api.example.com/auth/apple/callback)A minimal Express server that receives Apple's POST and redirects to your app:
const express = require('express');
const app = express();
const APP_SCHEME = 'myapp'; // Your app's URL scheme
app.use(express.urlencoded({ extended: true }));
app.post('/auth/apple/callback', (req, res) => {
const { id_token, code, state, user } = req.body;
const params = new URLSearchParams();
if (id_token) params.set('id_token', id_token);
if (code) params.set('code', code);
if (state) params.set('state', state);
if (user) params.set('user', user);
// Redirect to the app via deep link
res.redirect(302, `${APP_SCHEME}://auth/apple?${params.toString()}`);
});
app.listen(3000);
Make sure your app has a URL scheme configured in app.json:
{
"expo": {
"scheme": "myapp"
}
}
Create a route to handle the callback deep link. With Expo Router, add a file at app/auth/apple.tsx:
import { useEffect } from 'react';
import { Platform } from 'react-native';
import { useLocalSearchParams, router } from 'expo-router';
import { AppleSignInModule } from '@forward-software/react-auth-apple';
export default function AppleCallback() {
const params = useLocalSearchParams<{ id_token?: string; code?: string; user?: string }>();
useEffect(() => {
if (Platform.OS === 'android' && params.id_token) {
AppleSignInModule.handleCallback({
id_token: params.id_token,
code: params.code,
user: params.user,
});
}
router.replace('/login');
}, [params]);
return null;
}
When the deep link myapp://auth/apple?id_token=...&code=... arrives, Expo Router matches it to this route. The route calls handleCallback() to resolve the pending sign-in promise, then navigates to the login screen where the auth state updates.
Pass the androidRedirectUri in both the client and button config. On Android, use the Services ID as clientId (not your app bundle ID):
import { Platform } from 'react-native';
const ANDROID_REDIRECT_URI = 'https://api.example.com/auth/apple/callback';
const appleClient = new AppleAuthClient({
clientId: Platform.OS === 'android' ? 'com.example.app.service' : 'com.example.app',
storage,
...(Platform.OS === 'android' && { androidRedirectUri: ANDROID_REDIRECT_URI }),
});
<AppleSignInButton
config={{
clientId: Platform.OS === 'android' ? 'com.example.app.service' : 'com.example.app',
storage,
...(Platform.OS === 'android' && { androidRedirectUri: ANDROID_REDIRECT_URI }),
}}
onCredential={(credentials) => auth.login(credentials)}
onError={(error) => console.error(error)}
/>
Note: On iOS, the
clientIdshould be your App Bundle ID. On Android, it must be the Apple Services ID since the flow uses web-based OAuth.
AppleSignInButton)| Prop | Type | Default | Description |
|---|---|---|---|
config | AppleWebAuthConfig | required | Apple Sign-In configuration |
onCredential | (credentials) => void | required | Called with credentials on success |
onError | (error) => void | - | Called on error |
color | 'black' | 'white' | 'white-outline' | 'black' | Button color scheme |
type | 'sign-in' | 'continue' | 'sign-up' | 'sign-in' | Button label type |
label | string | Based on type | Custom label for localization |
width | number | auto | Button width in pixels |
height | number | 44 | Button height in pixels |
AppleSignInButton)| Prop | Type | Default | Description |
|---|---|---|---|
config | AppleNativeAuthConfig | required | Apple Sign-In configuration |
onCredential | (credentials) => void | required | Called with credentials on success |
onError | (error) => void | - | Called on error |
style | StyleProp<ViewStyle> | - | Additional button styles |
disabled | boolean | false | Disable the button |
color | 'black' | 'white' | 'black' | Button color scheme |
label | string | 'Sign in with Apple' | Custom label for localization |
You can use the SDK wrapper directly for custom flows:
// Web
import { loadAppleIdScript, initializeAppleAuth, signInWithApple } from '@forward-software/react-auth-apple/web/appleid';
await loadAppleIdScript();
initializeAppleAuth({
clientId: 'com.example.service',
scope: 'name email',
redirectURI: 'https://example.com/callback',
usePopup: true,
});
const response = await signInWithApple();
// React Native
import { Platform } from 'react-native';
import { AppleSignInModule } from '@forward-software/react-auth-apple';
AppleSignInModule.configure({ scopes: ['name', 'email'] });
const credentials = await AppleSignInModule.signIn();
// getCredentialState is iOS-only; it rejects with UNSUPPORTED on Android
if (Platform.OS === 'ios') {
const state = await AppleSignInModule.getCredentialState(credentials.user);
}
identityToken (similar to Google's idToken). The exp claim is extracted automatically for expiration tracking.identityToken and user ID. Store user info on your backend after the first login.getCredentialState(). This is used during token refresh instead of silent re-authentication.AppleAuthTokens - Token object stored after sign-inAppleAuthCredentials - Credentials passed to login()AppleFullName - Structured name (givenName, familyName, etc.)AppleWebAuthConfig - Web configurationAppleNativeAuthConfig - Native configurationAppleScope - 'name' | 'email'TokenStorage - Storage interface for persistenceAppleAuthClient - Implements AuthClient<AppleAuthTokens, AppleAuthCredentials>loadAppleIdScript() - Load the Apple JS SDKinitializeAppleAuth(config) - Initialize the SDKsignInWithApple() - Trigger sign-in flowAppleSignInModule.configure(config) - Configure the native moduleAppleSignInModule.signIn() - Trigger native sign-inAppleSignInModule.getCredentialState(userID) - Check credential state (iOS only)AppleSignInModule.handleCallback(params) - Complete a pending Android sign-in from a deep-link callbackAppleSignInModule.signOut() - Sign out (no-op, clears JS-side storage)MIT
FAQs
Apple Sign-In adapter for @forward-software/react-auth - Web and React Native/Expo
The npm package @forward-software/react-auth-apple receives a total of 10 weekly downloads. As such, @forward-software/react-auth-apple popularity was classified as not popular.
We found that @forward-software/react-auth-apple demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.