Swift 100.0%
1 1 0

Clone this repository

https://tangled.org/ngerakines.me/oauth-masterclass-swift
git@tangled.org:ngerakines.me/oauth-masterclass-swift

For self-hosted knots, clone URLs may differ based on your setup.

README.md

ATProtocol OAuth iOS Demo#

A complete iOS application demonstrating ATProtocol OAuth 2.1 authentication with PKCE, DPoP, and PAR. This app authenticates users with the Bluesky/ATProtocol network and demonstrates making authenticated XRPC requests.

Features#

  • ✅ Full OAuth 2.1 implementation with PKCE (Proof Key for Code Exchange)
  • ✅ DPoP (Demonstrating Proof of Possession) for token binding
  • ✅ PAR (Pushed Authorization Requests)
  • ✅ Secure token storage in iOS Keychain
  • ✅ Handle and DID resolution
  • ✅ ASWebAuthenticationSession for secure authentication
  • ✅ XRPC API calls (create post demo)
  • ✅ SwiftUI interface with MVVM pattern

Requirements#

  • iOS 14.0+
  • Xcode 14.0+
  • Swift 5.9+
  • A Bluesky account (create free at https://bsky.app)

Project Structure#

ATProtoOAuthDemo/
├── ATProtoOAuthDemoApp.swift       # Main app entry point
├── Info.plist                       # App configuration with URL scheme
├── Models/
│   ├── OAuthModels.swift           # OAuth data structures
│   ├── DIDDocument.swift           # DID document models
│   └── XRPCModels.swift            # XRPC request/response models
├── Authentication/
│   ├── AuthenticationManager.swift # Main auth coordinator
│   ├── OAuthClient.swift           # OAuth flow implementation
│   ├── PKCEGenerator.swift         # PKCE code generation
│   ├── DPoPGenerator.swift         # DPoP JWT generation
│   ├── IdentityResolver.swift      # Handle/DID resolution
│   └── KeychainManager.swift       # Secure token storage
├── Networking/
│   └── XRPCClient.swift            # XRPC API client
├── Views/
│   ├── ContentView.swift           # Root view
│   ├── LoginView.swift             # Login interface
│   ├── AuthenticatedView.swift     # Post-login view
│   └── CreatePostView.swift        # Create post demo
└── Utilities/
    └── Constants.swift              # App constants

Setup Instructions#

1. Create a New Xcode Project#

  1. Open Xcode
  2. Create a new iOS App project
  3. Product Name: "ATProtoOAuthDemo"
  4. Interface: SwiftUI
  5. Language: Swift
  6. Deployment Target: iOS 14.0 or later

2. Add Source Files#

Copy all the Swift files from the ATProtoOAuthDemo directory into your Xcode project, maintaining the folder structure.

3. Configure Info.plist#

The Info.plist file is already configured with the custom URL scheme me.ngerakines.atprotodemo. This is used for OAuth callbacks.

Important: If you change the bundle identifier, update the URL scheme in both:

  • Info.plist: Update CFBundleURLSchemes
  • Constants.swift: Update urlScheme constant

4. Build and Run#

  1. Select a simulator or device
  2. Press Cmd+R to build and run
  3. The app will launch and show the login screen

Usage#

Authenticating#

  1. Launch the app
  2. Enter a Bluesky handle (e.g., "yourname.bsky.social")
  3. Tap "Sign In with OAuth"
  4. You'll be redirected to the Bluesky authorization page
  5. Log in and approve the authorization
  6. You'll be redirected back to the app

Creating a Post#

  1. After authentication, tap "Create Post"
  2. Enter your post text (max 300 characters)
  3. Tap "Post"
  4. The post will be created via XRPC and appear on your Bluesky feed

Technical Details#

OAuth Flow#

  1. Handle Resolution: Converts handle to DID via .well-known/atproto-did
  2. DID Document Fetch: Retrieves user's DID document to find PDS
  3. Auth Server Discovery: Discovers OAuth server via PDS metadata
  4. PKCE Generation: Creates code verifier and S256 challenge
  5. PAR Request: Pushes authorization parameters to server
  6. User Authorization: Opens ASWebAuthenticationSession for user consent
  7. Token Exchange: Exchanges authorization code for tokens with PKCE
  8. Identity Verification: Verifies DID in token matches expected user

Security Features#

  • PKCE with S256: Prevents authorization code interception
  • DPoP: Binds tokens to specific key pairs to prevent replay attacks
  • State Parameter: Prevents CSRF attacks
  • Keychain Storage: All tokens stored securely in iOS Keychain
  • ASWebAuthenticationSession: Uses system browser for OAuth (not embedded WebView)

Development Mode#

The app runs in development mode by default (useDevelopmentMode = true in Constants.swift), which uses http://localhost as the client ID. This is allowed by ATProtocol for development purposes.

For production:

  1. Set useDevelopmentMode = false
  2. Host a client-metadata.json file at clientMetadataURL
  3. Update clientMetadataURL in Constants.swift

Customization#

Change URL Scheme#

  1. Update urlScheme in Constants.swift
  2. Update CFBundleURLSchemes in Info.plist
  3. Ensure both match your bundle identifier

Add More XRPC Methods#

Add new methods to XRPCClient.swift following the same pattern:

func yourMethod() async throws -> YourResponse {
    // Similar structure to createPost()
}

Troubleshooting#

"Sign-in was cancelled"#

  • User cancelled the OAuth flow
  • Check that the handle is valid

"Identity verification failed"#

  • DID mismatch between expected and actual
  • Try signing out and signing in again

"Could not obtain access tokens"#

  • Check network connection
  • Ensure handle is a valid Bluesky account
  • Check Xcode console for detailed errors

URL Scheme Issues#

  • Ensure Info.plist URL scheme matches Constants.swift
  • Verify the scheme is unique (use your bundle ID)
  • Check that OAuth callback URL in logs matches your redirect URI

Testing#

Test with a real Bluesky account:

  1. Create a free account at https://bsky.app
  2. Use your handle (e.g., "yourname.bsky.social") in the app
  3. Verify authentication completes successfully
  4. Test creating a post and check it appears on Bluesky

Architecture#

This app uses:

  • SwiftUI for the user interface
  • MVVM pattern with AuthenticationManager as ViewModel
  • Combine for reactive state management
  • async/await for asynchronous operations
  • No external dependencies - uses only native iOS frameworks

Resources#

License#

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing#

This is a reference implementation. Feel free to use it as a starting point for your own ATProtocol applications.