---
title: "Apple Sign-in Integration"
description: "Complete guide to implementing Sign in with Apple in Ring Platform"
locale: "en"
---
# Apple Sign-in Integration Guide

This guide provides a complete walkthrough for integrating **Sign in with Apple** into your Ring Platform application.

## Prerequisites Checklist

- ✅ Apple Developer Account ($99/year)
- ✅ App ID registered with Sign in with Apple capability
- ✅ Service ID configured for web authentication
- ✅ Private key (.p8 file) downloaded and secured
- ✅ Team ID from Apple Developer account

## Quick Setup (5 minutes)

If you already have your Apple credentials, here's the fastest way to get Apple Sign-in working:

### 1. Environment Variables

Add to your `.env.local`:

{`AUTH_APPLE_SECRET=`}

### 2. Generate JWT Token

Run our helper script:

{`cd scripts
node generate-apple-jwt.js`}

Or manually generate with Node.js:

{`import fs from 'fs';

const token = jwt.sign(
  {
    iss: 'X9EQDPCJU6', // Your Team ID
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 15777000,
    aud: 'https://appleid.apple.com',
    sub: 'com.sonoratek.ring-auth',
  },
  fs.readFileSync('AuthKey_YD444LWM9J.p8'),
  { algorithm: 'ES256', keyid: 'YD444LWM9J' }
);`}

### 3. Add to Your Login Component

{`import { signIn } from 'next-auth/react'
import { AppleIcon } from '@/components/icons'

export function LoginForm() {
  return (
    
       signIn('apple')}
        className="w-full flex items-center justify-center gap-2 bg-black text-white py-3 px-4 rounded-lg hover:bg-gray-800 transition-colors"
      >
        
        Continue with Apple
      
    
  )
}`}

That's it! Apple Sign-in is now active in your application.

## Complete Implementation Example

Here's a full example of Apple Sign-in integrated into a login component:

// components/auth/AppleSignInButton.tsx

{`'use client'

import { signIn, getSession } from 'next-auth/react'
import { useState } from 'react'
import { AppleIcon } from '@/components/icons'

interface AppleSignInButtonProps {
  className?: string
  size?: 'sm' | 'md' | 'lg'
  variant?: 'primary' | 'secondary'
}

export function AppleSignInButton({
  className = '',
  size = 'md',
  variant = 'primary'
}: AppleSignInButtonProps) {
  const [isLoading, setIsLoading] = useState(false)

  const handleAppleSignIn = async () => {
    try {
      setIsLoading(true)

      const result = await signIn('apple', {
        callbackUrl: '/dashboard',
        redirect: false
      })

      if (result?.error) {
        console.error('Apple sign-in error:', result.error)
        // Handle error (show toast, etc.)
      } else if (result?.url) {
        window.location.href = result.url
      }
    } catch (error) {
      console.error('Apple sign-in failed:', error)
    } finally {
      setIsLoading(false)
    }
  }

  const sizeClasses = {
    sm: 'py-2 px-3 text-sm',
    md: 'py-3 px-4 text-base',
    lg: 'py-4 px-6 text-lg'
  }

  const variantClasses = {
    primary: 'bg-black text-white hover:bg-gray-800 border-black',
    secondary: 'bg-white text-black border-gray-300 hover:bg-gray-50'
  }

  return (
    
      {isLoading ? (
        
      ) : (
        
      )}
      {isLoading ? 'Signing in...' : 'Continue with Apple'}
    
  )
}`}

## Integration with Existing Login Flow

Here's how to integrate Apple Sign-in with your existing authentication UI:

// components/auth/SocialLoginSection.tsx

{`'use client'

import { signIn } from 'next-auth/react'
import { AppleIcon, GoogleIcon } from '@/components/icons'

export function SocialLoginSection() {
  const handleProviderSignIn = async (provider: 'google' | 'apple') => {
    try {
      await signIn(provider, {
        callbackUrl: '/onboarding',
        redirect: true
      })
    } catch (error) {
      console.error(`${provider} sign-in error:`, error)
    }
  }

  return (
    
      
        
          
        
        
          Or continue with
        
      

      
         handleProviderSignIn('google')}
          className="flex items-center justify-center gap-2 py-2.5 px-4 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
        >
          
          Google
        

         handleProviderSignIn('apple')}
          className="flex items-center justify-center gap-2 py-2.5 px-4 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
        >
          
          Apple
        
      
    
  )
}`}

## Handling Apple Sign-in Callbacks

// app/auth/callback/apple/page.tsx

{`'use client'

import { useEffect, useState } from 'react'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'

export default function AppleCallback() {
  const { data: session, status } = useSession()
  const router = useRouter()
  const [error, setError] = useState(null)

  useEffect(() => {
    if (status === 'loading') return

    if (session?.user) {
      // Successful sign-in
      router.push('/dashboard')
    } else {
      // Check for error in URL params
      const urlParams = new URLSearchParams(window.location.search)
      const errorParam = urlParams.get('error')

      if (errorParam) {
        setError(`Authentication failed: ${errorParam}`)
      }
    }
  }, [session, status, router])

  if (status === 'loading') {
    return (
      
        
          
          Completing sign-in...
        
      
    )
  }

  if (error) {
    return (
      
        
          ⚠️ {error}
           router.push('/login')}
            className="px-4 py-2 bg-black text-white rounded-lg hover:bg-gray-800"
          >
            Try Again
          
        
      
    )
  }

  return null
}`}

## Customizing Apple Sign-in Experience

### Styling Options

// Custom styled Apple button

{`export function CustomAppleButton() {
  return (
     signIn('apple')}
      className="group relative w-full flex items-center justify-center py-3 px-4 border border-gray-300 rounded-lg hover:border-gray-400 transition-colors"
    >
      
        
          
        
      
      Sign in with Apple
    
  )
}`}

## Testing Apple Sign-in

### Development Testing

1. **Use TestFlight**: Apple provides TestFlight for testing Sign in with Apple
2. **Sandbox Environment**: Apple provides sandbox accounts for testing
3. **Development Certificates**: Use development certificates for testing

### Production Testing

1. **App Store Review**: Apple reviews Sign in with Apple implementation
2. **Privacy Policy**: Ensure your privacy policy mentions Sign in with Apple
3. **User Consent**: Verify users understand what data Apple shares

## Troubleshooting Common Issues

### "Invalid Client" Error

// Check your environment variables

{`console.log('APPLE_ID:', process.env.AUTH_APPLE_ID)
console.log('APPLE_SECRET length:', process.env.AUTH_APPLE_SECRET?.length)

// Verify Service ID matches
const expectedServiceId = 'com.sonoratek.ring-auth'
const actualServiceId = process.env.AUTH_APPLE_ID

if (actualServiceId !== expectedServiceId) {
  console.error('Service ID mismatch!')
}`}

### JWT Token Issues

// Validate JWT structure

{`import jwt from 'jsonwebtoken'

function validateAppleJWT(token: string) {
  try {
    const decoded = jwt.decode(token, { complete: true })
    console.log('JWT Header:', decoded?.header)
    console.log('JWT Payload:', decoded?.payload)

    // Check expiration
    const exp = decoded?.payload?.exp
    if (exp && exp < Date.now() / 1000) {
      console.error('JWT token expired!')
    }
  } catch (error) {
    console.error('Invalid JWT:', error)
  }
}`}

### Domain Verification

Ensure your domain is properly configured in Apple Developer Portal:

1. Go to your Service ID configuration
2. Add your domain to "Domains and Subdomains"
3. Add return URLs for your application
4. Verify domain ownership with Apple's verification file

## Performance Optimization

### Caching Strategies

{`// Cache JWT tokens (regenerate every 5 months instead of 6)
const JWT_CACHE_DURATION = 5 * 30 * 24 * 60 * 60 * 1000 // 5 months

export function getCachedAppleJWT() {
  const cached = localStorage.getItem('apple-jwt')
  const cacheTime = localStorage.getItem('apple-jwt-time')

  if (cached && cacheTime) {
    const age = Date.now() - parseInt(cacheTime)
    if (age < JWT_CACHE_DURATION) {
      return cached
    }
  }

  // Generate new token
  const newToken = generateAppleJWT()
  localStorage.setItem('apple-jwt', newToken)
  localStorage.setItem('apple-jwt-time', Date.now().toString())

  return newToken
}`}

## Security Best Practices

1. **Key Rotation**: Rotate private keys regularly
2. **Environment Isolation**: Use different keys for dev/staging/production
3. **Audit Logging**: Log all authentication attempts
4. **Rate Limiting**: Implement rate limiting on sign-in endpoints
5. **Token Validation**: Always validate tokens server-side

## Migration from Other Providers

If migrating from Google-only authentication:

{`// Before (Google only)
 signIn('google')}>
  Sign in with Google

// After (Multi-provider)

   signIn('google')}>
    Sign in with Google
  
   signIn('apple')}>
    Sign in with Apple
  
`}

This implementation provides a seamless, secure, and user-friendly Apple Sign-in experience for your Ring Platform users.
