CISCODE-MA/authpackage_Swift
Production-ready authentication for Swift apps
AuthPackage (iOS / Swift) β Core + Drop-in UI
Production-ready authentication for Swift apps, with a strongly-typed core client and a drop-in SwiftUI flow.
Includes email/password auth, password reset, and OAuth (Microsoft, Google, Facebook).
This README is the step-by-step integration guide. It covers architecture, install, configuration, OAuth setup, CI/CD, troubleshooting, and security.
π Table of Contents
- Architecture
- Requirements
- Install & Run (60s)
- Configuration Reference
- App URL Scheme (Info.plist)
- OAuth Providers (Microsoft, Google, Facebook)
- Backend Contract
- Using the Core API Directly
- Token Storage & Refresh
- Post-Login Deeplink
- Troubleshooting
- Security Checklist
- CI/CD (Azure Pipelines)
- Versioning & Releases
- Changelog
- Contributing
- License
π Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββ
β Your App β
β SwiftUI navigation, state, feature screens β
ββββββββββββββββββββββββ²βββββββββββββββββββββββββ
β consumes
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββ
β AuthPackageUI β
β Prebuilt flows (Login, Register, Forgot/Resetβ
β Theming via CSS-like vars, routing helpers β
ββββββββββββββββββββββββ²βββββββββββββββββββββββββ
β uses
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββ
β AuthPackage β
β AuthClient facade, services, endpoints β
β NetworkClient (URLSession) β
ββββββββββββββββββββββββ²βββββββββββββββββββββββββ
β HTTP (JSON/JWT)
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββ
β Backend β
β Express API + Passport (local + OAuth) β
β Issues app JWTs, stores refresh, deep-links β
βββββββββββββββββββββββββββββββββββββββββββββββββ
- Core (
AuthPackage) β typed API for login, refresh, logout, register, reset, OAuth. - UI (
AuthPackageUI) β SwiftUI screens using the core client; drop-in βauth flow.β - Extensibility β use Core without UI, or override visuals via theme tokens.
π Requirements
- Xcode 16+
- iOS 15.0+
- Swift 5.9+
- A reachable backend implementing the contract below
β‘ Install & Run (60s)
1) Add package (SPM)
Xcode β File β Add Package Dependenciesβ¦ β
URL: https://github.com/CISCODE-MA/AuthPackage-Swift.git β Products: AuthPackage, AuthPackageUI β Version: Up to Next Major from 1.0.0.
2) Register your URL scheme (Info.plist)
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>authdemo</string> <!-- change to your scheme -->
</array>
</dict>
</array>3) Show the drop-in UI (persistent client + post-login deeplink)
import SwiftUI
import AuthPackage
import AuthPackageUI
enum AuthSecrets {
static let baseURL = URL(string: "https://YOUR-BACKEND.example.com")!
static let scheme = "authdemo" // must match Info.plist
static let keychainService = "com.example.AuthDemo"
static let keychainAccount = "auth"
}
enum AuthDependencies {
static let client: AuthClientProtocol = {
let cfg = AuthConfiguration(
baseURL: AuthSecrets.baseURL,
refreshUsesCookie: true,
redirectScheme: AuthSecrets.scheme,
microsoftEnabled: true,
googleEnabled: true,
facebookEnabled: true
)
let store = KeychainTokenStore(
service: AuthSecrets.keychainService,
account: AuthSecrets.keychainAccount
)
return AuthClient(config: cfg, tokenStore: store)
}()
}
@main
struct MyApp: App {
private let client = AuthDependencies.client
@State private var showHome = false
var body: some Scene {
WindowGroup {
Group {
if showHome {
Text("Home") // replace with your HomeView
} else {
AuthPackageUI.makeRoot(
config: AuthUIConfig(
baseURL: AuthSecrets.baseURL,
appScheme: AuthSecrets.scheme,
microsoftEnabled: true,
googleEnabled: true,
facebookEnabled: true,
cssVariables: """
:root {
--authui-primary-color: #B53CC2;
--authui-accent-color: #D078FF;
--authui-background-color: #1F1F1F;
--authui-text-color: #ffffff;
--authui-corner-radius: 8;
--authui-spacing: 14;
--authui-font-family: Poppins;
--authui-title-size: 28;
--authui-body-size: 17;
}
""",
postLoginDeeplink: URL(string: "\(AuthSecrets.scheme)://home")
),
client: client
)
}
}
.onAppear {
if let t = client.tokens?.accessToken, !t.isEmpty { showHome = true }
}
.onOpenURL { url in
guard url.scheme == AuthSecrets.scheme else { return }
if url.host == "home" { showHome = true }
}
}
}
}Device tip: real devices canβt reach
localhost. Use your LAN IP or an HTTPS tunnel (ngrok, Cloudflare), and ensure your backend + provider apps use the same host in their redirect URLs.
βοΈ Configuration Reference
AuthUIConfig (UI module)
| Property | Type | Notes |
|---|---|---|
baseURL |
URL |
Backend origin |
appScheme |
String |
Must match Info.plist scheme |
microsoftEnabled / googleEnabled / facebookEnabled |
Bool |
Toggle OAuth buttons |
postLoginDeeplink |
URL? |
Open after login (optional) |
cssVariables |
String? |
CSS-like theme tokens |
AuthConfiguration (Core module)
| Property | Type | Notes |
|---|---|---|
baseURL |
URL |
Backend origin |
refreshUsesCookie |
Bool |
Enable cookie-based refresh |
redirectScheme |
String? |
Required for OAuth flows |
microsoftEnabled / googleEnabled / facebookEnabled |
Bool |
Enable providers |
π App URL Scheme (Info.plist)
Already shown in Install & Run. Ensure your scheme matches
appSchemeand provider redirect URIs.
π OAuth Providers (Microsoft, Google, Facebook)
All follow the same flow:
- App opens backend route with
redirect=authdemo://auth/callback. - Provider login β backend callback β backend mints tokens.
- Backend 302 β
authdemo://auth/callback?...with tokens. - Package saves tokens; user authenticated.
- Microsoft β Azure AD App with callback URLs for localhost, LAN IP, tunnel. Add optional claims (
email,upn,preferred_username). - Google β GCP OAuth client with redirect URIs; scopes:
email,profile. - Facebook β App in Meta Developer; valid redirect URIs; permissions:
email.
π‘ Backend Contract
POST /api/auth/clients/loginβ{ accessToken, refreshToken }POST /api/auth/refresh-tokenβ{ accessToken }POST /api/auth/logoutPOST /api/auth/forgot-passwordPOST /api/auth/reset-passwordPOST /api/clients/registerGET /api/auth/{provider}?redirect=<scheme>://auth/callbackGET /api/auth/{provider}/callbackβ 302 with tokensPOST /api/auth/{provider}/exchange(native SDK β app tokens)
Keep endpoint names consistent across login/register (either use
/api/auth/**or/api/clients/**for both).
π Using the Core API Directly
let client = AuthClient(config: core)
// Email/password
let claims = try await client.login(email: "user@example.com", password: "Secret123!")
// OAuth
let window = UIApplication.shared.connectedScenes
.compactMap { ($0 as? UIWindowScene)?.keyWindow }.first!
let googleClaims = try await client.loginWithGoogle(from: window)
// Refresh
let access = try await client.refreshIfNeeded()
// Logout
try await client.logout()π Token Storage & Refresh
- Default β in-memory
- Production β provide a
TokenStorebacked by Keychain refreshIfNeeded()uses refresh token or HttpOnly cookie
π Post-Login Deeplink
If postLoginDeeplink is set, the UI opens it after success. Handle in your app (authdemo://home) to route.
π©Ί Troubleshooting
- AADSTS500113 β missing redirect in Azure β add exact URL
- OAuth sheet loops β hostname mismatch or missing claims
- No deep-link β Info.plist scheme mismatch
- Device fails, simulator works β device canβt reach
localhostβ use LAN IP/tunnel - OAuth user missing email β add scopes/permissions in provider
π Security Checklist
- Use HTTPS in production
- Store tokens in Keychain
- Rotate refresh tokens
- Donβt log tokens
- Use unique app schemes
- Lock down backend CORS + callbacks
βοΈ CI/CD (Azure Pipelines)
- Run SwiftPM tests on macOS
- Export LCOV coverage
- Publish zipped source artifact
- Optionally: publish to Azure Artifacts or run Sonar analysis
π¦ Versioning & Releases
- Semantic Versioning (SemVer)
- Stable releases tagged
vX.Y.Z - Pre-releases via
releasebranch βNEXT_MINOR.0-rc.YYYYMMDD.BUILDID - See CHANGELOG.md for details
First Official Release (resetting history of tags)
If you want to make this repository's first official open-source release and discard all previous tags:
β οΈ This rewrites tag history only; it does not rewrite commit history.
- Delete old tags locally:
git tag -l | xargs -n 1 git tag -d - Delete old tags on origin (GitHub):
git ls-remote --tags origin | awk '{print $2}' | sed "s#refs/tags/##" | xargs -n 1 -I {} git push origin :refs/tags/{}
- Create the first official tag and push it:
export VERSION=1.0.0 git tag -a "v${VERSION}" -m "AuthPackage v${VERSION} β first public release" git push origin "refs/tags/v${VERSION}"
- Update the changelog: Move items from [Unreleased] into [v${VERSION} - YYYY-MM-DD] in
CHANGELOG.md.
If your default branch is protected, grant your CI GitHub App permission to push the tag or create the tag via a PR merged commit.
Changelog
We maintain a human-friendly changelog in CHANGELOG.md following the
Keep a Changelog format and Semantic Versioning.
- Latest release: See the repo Releases page.
- Unreleased changes: Tracked under the [Unreleased] section until a version is tagged.
π€ Contributing
Please read the Contributing Guide before opening an issue or pull request. It explains our branching model, coding standards, commit message conventions, and how to run tests locally.
- Pull Requests: Open a PR targeting the default branch and ensure CI checks pass.
- Conventional Commits: Use the Conventional Commits format (e.g.
feat:,fix:,docs:). This powers automated changelog generation. - Code of Conduct: Be respectful and constructive. See CODE_OF_CONDUCT.md.
π License
Licensed under the MIT License.
Β© 2025 AuthPackage contributors