Type-safe Swift definitions for identity authentication and management with dependency injection and URL routing support.
- Overview
- Installation
- Quick Start
- Usage Examples
- Architecture
- Requirements
- Related Packages
- Contributing
- License
swift-identities-types provides comprehensive type definitions for building identity management systems, including:
- Authentication: Credentials, tokens, API keys, and OAuth
- Identity Creation: Two-step creation with email verification
- Password Management: Reset and change flows
- Email Management: Email change with confirmation
- Identity Deletion: Request and confirm deletion with safety checks
- Reauthorization: Token-based sensitive operation verification
- Multi-Factor Authentication: TOTP, SMS, Email, WebAuthn, and backup codes
- Type-Safe Routing: URLRouting integration for compile-time route validation
- Dependency Injection: Using swift-dependencies for testability
This is a types-only package. For complete implementations, see the Related Packages section below.
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/coenttb/swift-identities-types", from: "0.1.0")
]import IdentitiesTypesThe Identity type provides access to all identity management operations through a unified interface:
@Dependency(\.identity) var identity
// Authenticate with credentials
let response = try await identity.login(
username: "[email protected]",
password: "password123"
)
// Create new identity
try await identity.create.request(
email: "[email protected]",
password: "securePassword123"
)
try await identity.create.verify(
email: "[email protected]",
token: "verification-token"
)import IdentitiesTypes
@Dependency(\.identity) var identity
// Using convenience method
let response = try await identity.login(
username: "[email protected]",
password: "password123"
)
print("Access token: \(response.accessToken)")
print("Refresh token: \(response.refreshToken)")
// Using credentials object
let credentials = Identity.Authentication.Credentials(
username: "[email protected]",
password: "password123"
)
let response = try await identity.authenticate.credentials(credentials)// Validate access token
try await identity.login(accessToken: "jwt-access-token")
// Refresh expired token
let newTokens = try await identity.login(refreshToken: "jwt-refresh-token")// Authenticate with API key
let response = try await identity.login(apiKey: "api-key-string")The creation process is a two-step flow with email verification:
// Step 1: Request identity creation
try await identity.create.request(
email: "[email protected]",
password: "securePassword123"
)
// Step 2: Verify email with token
try await identity.create.verify(
email: "[email protected]",
token: "verification-token-from-email"
)
// Now the user can authenticate
let response = try await identity.login(
username: "[email protected]",
password: "securePassword123"
)// Step 1: Request password reset
try await identity.password.reset.request(
email: "[email protected]"
)
// Step 2: Confirm with token from email
try await identity.password.reset.confirm(
newPassword: "newSecurePassword123",
token: "reset-token-from-email"
)// Change password while authenticated
try await identity.password.change.request(
currentPassword: "currentPassword123",
newPassword: "newSecurePassword456"
)// Request email change
let result = try await identity.email.change.request(
newEmail: "[email protected]"
)
// Confirm email change with token
let response = try await identity.email.change.confirm(
token: "confirmation-token-from-email"
)
// Response contains new access and refresh tokens// Request deletion (requires recent authentication)
try await identity.delete.request(
reauthToken: "fresh-auth-token"
)
// Confirm deletion (irreversible)
try await identity.delete.confirm()
// Or cancel the deletion request
try await identity.delete.cancel()For sensitive operations requiring fresh authentication:
import JWT
// Reauthorize with password
let reauthToken: JWT = try await identity.reauthorize.reauthorize(
password: "currentPassword123"
)
// Use the reauth token for sensitive operations
try await identity.delete.request(
reauthToken: reauthToken.compactSerialization()
)Generate and parse URLs with compile-time safety:
@Dependency(\.identity.router) var router
// Generate URL for login API
let api: Identity.API = .authenticate(.credentials(
.init(username: "[email protected]", password: "password123")
))
let request = try router.request(for: .api(api))
// Produces: POST /api/authenticate
// Generate URL for password reset view
let viewRoute = Identity.Route.passwordReset
let viewRequest = try router.request(for: viewRoute)
// Produces: GET /password/reset/request
// Parse incoming request
let match = try router.match(request: incomingRequest)
if match.is(\.authenticate.api.credentials) {
let credentials = match.authenticate?.api?.credentials
// Handle credential authentication
}When MFA is enabled, the identity provides access to MFA operations:
if let mfa = identity.mfa {
// Check MFA status
let status = try await mfa.status.client.get()
// Setup TOTP (authenticator app)
let secret = try await mfa.totp.client.setup()
try await mfa.totp.client.verify(code: "123456")
// Generate backup codes
let codes = try await mfa.backupCodes.client.generate()
}Create mock implementations for testing:
import Testing
import DependenciesTestSupport
@Test
func testAuthentication() async throws {
// Use test dependency key
try await Identity._TestDatabase.Helper.withIsolatedDatabase {
@Dependency(\.identity) var identity
// Create test user
try await identity.create.request(
email: "[email protected]",
password: "testPassword123"
)
try await identity.create.verify(
email: "[email protected]",
token: "[email protected]"
)
// Test authentication
let response = try await identity.login(
username: "[email protected]",
password: "testPassword123"
)
#expect(!response.accessToken.isEmpty)
#expect(!response.refreshToken.isEmpty)
}
}The package follows a domain-first architecture where business capabilities are primary:
Identity // Main namespace
├── authenticate: Authentication // Authentication operations
│ ├── client: Client // Authentication client
│ ├── token: Token.Client // Token operations
│ └── router: Router<Route> // URL routing
├── create: Creation // Identity creation
├── delete: Deletion // Identity deletion
├── email: Email // Email management
├── password: Password // Password operations
│ ├── reset: Reset // Password reset flow
│ └── change: Change // Password change flow
├── logout: Logout // Session termination
├── reauthorize: Reauthorization // Fresh auth for sensitive ops
├── mfa: MFA? // Optional MFA support
│ ├── totp: TOTP // Authenticator app
│ ├── sms: SMS // SMS verification
│ ├── email: Email // Email verification
│ ├── webauthn: WebAuthn // Security keys
│ ├── backupCodes: BackupCodes // Recovery codes
│ └── status: Status // MFA status queries
└── oauth: OAuth? // Optional OAuth supportEach domain contains:
- Client: Operations interface using @DependencyClient
- Router: Type-safe URL routing using URLRouting
- API: API endpoint definitions
- Route: Combined API and View routes
- Request/Response types: Strongly-typed data models
All types are:
Sendablefor Swift 6 strict concurrencyCodablefor JSON serializationEquatablefor testing- Validated at compile-time with URLRouting
- Swift 6.0+
- macOS 14.0+ / iOS 17.0+
- Strict concurrency mode enabled
- swift-authenticating: A Swift package for type-safe HTTP authentication with URL routing integration.
- swift-jwt: A Swift package for creating, signing, and verifying JSON Web Tokens.
- swift-types-foundation: A Swift package bundling essential type-safe packages for domain modeling.
- swift-identities: The Swift library for identity authentication and management.
- swift-identities-github: A Swift package integrating GitHub OAuth with swift-identities.
- swift-identities-mailgun: A Swift package integrating Mailgun with swift-identities.
- pointfreeco/swift-dependencies: A dependency management library for controlling dependencies in Swift.
Contributions are welcome. Please open an issue or submit a pull request.
This project is licensed under the Apache 2.0 License. See LICENSE for details.