The YouVersion Platform SDK provides secure OAuth 2.0 authentication using PKCE (Proof Key for Code Exchange). This enables users to sign in with their YouVersion account and access personalized features.
Why Authentication?
With authentication, your app can:
Access user profile information (name, avatar)
Read and write user highlights and bookmarks
Personalize the reading experience
Sync user data across devices
Setup
Step 1: Register Your App
First, register your application at the YouVersion Developer Portal to obtain:
App Key : Your application identifier
Redirect URI : Where users return after authentication
Wrap your app with YouVersionProvider and enable authentication:
import { YouVersionProvider } from '@youversion/platform-react-hooks' ;
function App () {
return (
< YouVersionProvider
appKey = "your-app-key"
includeAuth = { true }
authRedirectUrl = "https://yourapp.com/callback"
>
< YourApp />
</ YouVersionProvider >
);
}
The authRedirectUrl must match exactly what you registered in the Developer Portal, including the protocol (http/https) and path.
Using the useYVAuth Hook
The useYVAuth hook provides complete authentication functionality:
import { useYVAuth } from '@youversion/platform-react-hooks' ;
function AuthExample () {
const { auth , userInfo , signIn , signOut } = useYVAuth ();
// Check authentication state
if ( auth . isLoading ) {
return < div > Loading... </ div > ;
}
if ( ! auth . isAuthenticated ) {
return (
< button onClick = { () => signIn () } >
Sign In with YouVersion
</ button >
);
}
// User is authenticated
return (
< div >
< p > Welcome, { userInfo ?. name } ! </ p >
< img src = { userInfo ?. getAvatarUrl ( 64 , 64 ) } alt = "Avatar" />
< button onClick = { signOut } > Sign Out </ button >
</ div >
);
}
Authentication Flow
Step 1: Sign In
Initiate the sign-in flow:
import { useYVAuth } from '@youversion/platform-react-hooks' ;
function SignInButton () {
const { signIn , auth } = useYVAuth ();
const handleSignIn = async () => {
try {
await signIn ({
redirectUrl: 'https://yourapp.com/callback' ,
scopes: [ 'profile' ], // Optional: specify required scopes
});
// User will be redirected to YouVersion for authentication
} catch ( error ) {
console . error ( 'Sign in failed:' , error );
}
};
return (
< button onClick = { handleSignIn } disabled = { auth . isLoading } >
{ auth . isLoading ? 'Signing in...' : 'Sign In' }
</ button >
);
}
Step 2: Handle the Callback
Create a callback page to process the authentication response:
// app/callback/page.tsx (Next.js example)
'use client' ;
import { useEffect , useState } from 'react' ;
import { useRouter } from 'next/navigation' ;
import { useYVAuth } from '@youversion/platform-react-hooks' ;
export default function CallbackPage () {
const router = useRouter ();
const { processCallback } = useYVAuth ();
const [ error , setError ] = useState < string | null >( null );
useEffect (() => {
const handleCallback = async () => {
try {
const result = await processCallback ();
if ( result ) {
console . log ( 'Authentication successful:' , result . name );
// Redirect to main app
router . push ( '/' );
} else {
setError ( 'Authentication failed' );
}
} catch ( err ) {
setError ( err instanceof Error ? err . message : 'Unknown error' );
}
};
handleCallback ();
}, [ processCallback , router ]);
if ( error ) {
return (
< div >
< h2 > Authentication Error </ h2 >
< p > { error } </ p >
< a href = "/" > Return to home </ a >
</ div >
);
}
return < div > Completing sign in... </ div > ;
}
Step 3: Sign Out
Clear the user session:
function SignOutButton () {
const { signOut } = useYVAuth ();
return (
< button onClick = { signOut } >
Sign Out
</ button >
);
}
The userInfo object provides:
import { useYVAuth } from '@youversion/platform-react-hooks' ;
function UserProfile () {
const { userInfo , auth } = useYVAuth ();
if ( ! auth . isAuthenticated || ! userInfo ) {
return < div > Please sign in </ div > ;
}
return (
< div >
{ /* Basic Info */ }
< h2 > { userInfo . name } </ h2 >
< p > User ID: { userInfo . id } </ p >
{ /* Avatar with specific size */ }
< img
src = { userInfo . getAvatarUrl ( 128 , 128 ) }
alt = { ` ${ userInfo . name } 's avatar` }
width = { 128 }
height = { 128 }
/>
{ /* Multiple avatar sizes */ }
< div >
< img src = { userInfo . getAvatarUrl ( 32 , 32 ) } alt = "Small" />
< img src = { userInfo . getAvatarUrl ( 64 , 64 ) } alt = "Medium" />
< img src = { userInfo . getAvatarUrl ( 256 , 256 ) } alt = "Large" />
</ div >
</ div >
);
}
Authentication Scopes
Request specific permissions with scopes:
// Request profile access
await signIn ({
scopes: [ 'profile' ],
});
// Request multiple scopes
await signIn ({
scopes: [ 'profile' , 'highlights' ],
});
Available Scopes
profile : Basic user information (name, avatar)
highlights : Read/write user highlights
bookmarks : Read/write user bookmarks
The openid scope is automatically included and doesn’t need to be specified.
Protected Routes
Protect routes that require authentication:
import { useYVAuth } from '@youversion/platform-react-hooks' ;
import { useRouter } from 'next/navigation' ;
import { useEffect } from 'react' ;
function ProtectedRoute ({ children } : { children : React . ReactNode }) {
const { auth } = useYVAuth ();
const router = useRouter ();
useEffect (() => {
if ( ! auth . isLoading && ! auth . isAuthenticated ) {
router . push ( '/sign-in' );
}
}, [ auth . isLoading , auth . isAuthenticated , router ]);
if ( auth . isLoading ) {
return < div > Loading... </ div > ;
}
if ( ! auth . isAuthenticated ) {
return null ;
}
return <> { children } </> ;
}
// Usage
function HighlightsPage () {
return (
< ProtectedRoute >
< MyHighlights />
</ ProtectedRoute >
);
}
Error Handling
Handle authentication errors gracefully:
import { useYVAuth } from '@youversion/platform-react-hooks' ;
function RobustAuth () {
const { auth , signIn } = useYVAuth ();
const handleSignIn = async () => {
try {
await signIn ();
} catch ( error ) {
if ( error instanceof Error ) {
// Handle specific error types
if ( error . message . includes ( 'redirectUrl' )) {
console . error ( 'Configuration error:' , error . message );
} else {
console . error ( 'Authentication error:' , error . message );
}
}
}
};
if ( auth . error ) {
return (
< div >
< p > Authentication error: { auth . error . message } </ p >
< button onClick = { handleSignIn } > Try Again </ button >
</ div >
);
}
return < button onClick = { handleSignIn } > Sign In </ button > ;
}
Complete Example
Here’s a full authentication implementation:
import { YouVersionProvider } from '@youversion/platform-react-hooks' ;
import { useYVAuth } from '@youversion/platform-react-hooks' ;
// Main App
function App () {
return (
< YouVersionProvider
appKey = { process . env . NEXT_PUBLIC_YVP_APP_KEY ! }
includeAuth = { true }
authRedirectUrl = { process . env . NEXT_PUBLIC_REDIRECT_URI ! }
>
< AuthAwareApp />
</ YouVersionProvider >
);
}
// Authentication-aware component
function AuthAwareApp () {
const { auth , userInfo , signIn , signOut } = useYVAuth ();
if ( auth . isLoading ) {
return (
< div className = "loading" >
< h2 > Loading... </ h2 >
</ div >
);
}
if ( ! auth . isAuthenticated ) {
return (
< div className = "sign-in" >
< h1 > Welcome </ h1 >
< button onClick = { () => signIn ({ scopes: [ 'profile' ] }) } >
Sign In with YouVersion
</ button >
</ div >
);
}
return (
< div className = "app" >
< header >
< div className = "user-info" >
< img
src = { userInfo ?. getAvatarUrl ( 40 , 40 ) }
alt = "Avatar"
className = "avatar"
/>
< span > { userInfo ?. name } </ span >
</ div >
< button onClick = { signOut } > Sign Out </ button >
</ header >
< main >
{ /* Your app content */ }
</ main >
</ div >
);
}
Using with BibleReader
The BibleReader component automatically shows authentication UI when enabled:
import { YouVersionProvider } from '@youversion/platform-react-hooks' ;
import { BibleReader } from '@youversion/platform-react-ui' ;
function BibleApp () {
return (
< YouVersionProvider
appKey = "your-app-key"
includeAuth = { true } // Enables auth UI in BibleReader
authRedirectUrl = "https://yourapp.com/callback"
>
< div style = { { height: '100vh' } } >
< BibleReader.Root defaultBook = "JHN" defaultChapter = "3" defaultVersionId = { 3034 } >
< BibleReader.Content />
< BibleReader.Toolbar />
{ /* User menu appears automatically when includeAuth is true */ }
</ BibleReader.Root >
</ div >
</ YouVersionProvider >
);
}
Security Best Practices
Never commit your app key to version control. Use environment variables: # .env.local
NEXT_PUBLIC_YVP_APP_KEY = your-app-key-here
NEXT_PUBLIC_REDIRECT_URI = https://yourapp.com/callback
Always use HTTPS for your redirect URI in production: // ✅ Good
authRedirectUrl = "https://yourapp.com/callback"
// ❌ Bad (production)
authRedirectUrl = "http://yourapp.com/callback"
// ✅ OK (local development)
authRedirectUrl = "http://localhost:3000/callback"
The SDK handles token validation automatically, but be aware:
Access tokens expire after a period
The SDK refreshes tokens automatically
Sign out clears all tokens from storage
Troubleshooting
”redirectUrl is required” Error
Make sure you provide authRedirectUrl to the provider or redirectUrl to signIn():
// Option 1: Provider-level (recommended)
< YouVersionProvider authRedirectUrl = "https://yourapp.com/callback" />
// Option 2: Per sign-in
await signIn ({ redirectUrl: 'https://yourapp.com/callback' });
Callback Not Working
Verify:
Redirect URI matches exactly (including trailing slashes)
URI is registered in Developer Portal
Callback page calls processCallback()
User Info Not Available
Ensure:
User has completed the callback flow
processCallback() has been called
includeAuth={true} in provider
Next Steps
Fetching Data Learn about data fetching hooks
Building a Reader Create a complete reading experience