Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ on:

jobs:
build-macos:
name: Build & Test (macOS)
name: Build (macOS)
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Build & Test macOS
- name: Build macOS
run: bash scripts/build-macos.sh

test-app:
Expand All @@ -26,6 +26,8 @@ jobs:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Run swift test
run: swift test
- name: Write .env file
env:
ENV_B64: ${{ secrets.ENV_B64 }}
Expand All @@ -38,6 +40,7 @@ jobs:
-testPlan TestPlan \
-destination 'platform=macOS' \
CODE_SIGN_IDENTITY=- \
CODE_SIGN_ENTITLEMENTS="" \
DEVELOPMENT_TEAM=""

test-app-ios:
Expand Down Expand Up @@ -77,6 +80,7 @@ jobs:
-testPlan TestPlan \
-destination 'platform=iOS Simulator,id=${{ steps.sim.outputs.sim_id }}' \
CODE_SIGN_IDENTITY=- \
CODE_SIGN_ENTITLEMENTS="" \
DEVELOPMENT_TEAM=""

build-ios:
Expand Down
8 changes: 4 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import PackageDescription
let package = Package(
name: "RxAuthSwift",
platforms: [
.iOS(.v18),
.macOS(.v15),
.iOS(.v26),
.macOS(.v26),
],
products: [
.library(
Expand All @@ -20,13 +20,13 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.0")
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"),
],
targets: [
.target(
name: "RxAuthSwift",
dependencies: [
.product(name: "Logging", package: "swift-log")
.product(name: "Logging", package: "swift-log"),
]
),
.target(
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,37 @@ let config = RxAuthConfiguration(
)
```

For native macOS sign-in, `RxSignInView` renders username/password fields instead of launching a browser. Password sign-in posts an OAuth password-grant request directly to `nativePasswordTokenPath` or `tokenPath` when no native override is provided:

```swift
let config = RxAuthConfiguration(
issuer: "https://auth.example.com",
clientID: "your-client-id",
redirectURI: "yourapp://callback",
nativePasswordTokenPath: "/api/oauth/token",
nativeSignupPath: "/api/oauth/signup"
)
```

The signup endpoint receives JSON with `client_id`, `username`, `password`, optional `name`, and `scope`, and should return the same token JSON as the OAuth token endpoint.

Passkey sign-in and signup are enabled when the matching endpoint pairs are configured:

```swift
let config = RxAuthConfiguration(
issuer: "https://auth.example.com",
clientID: "your-client-id",
redirectURI: "yourapp://callback",
passkeyChallengePath: "/api/passkeys/authentication/options",
passkeyVerificationPath: "/api/passkeys/authentication/verify",
passkeyRegistrationChallengePath: "/api/passkeys/registration/options",
passkeyRegistrationVerificationPath: "/api/passkeys/registration/verify",
passkeyRelyingPartyIdentifier: "auth.example.com"
)
```

The authentication challenge endpoint should return a base64url WebAuthn challenge, optional `requestID`, optional relying party identifier, and optional allowed credential IDs. The registration challenge endpoint should return a base64url challenge and user ID, plus optional `requestID`, username, and relying party identifier. Verification endpoints receive the platform passkey assertion or registration payload and return the same token JSON as the OAuth token endpoint.

### 2. Create OAuthManager

```swift
Expand Down
23 changes: 23 additions & 0 deletions Sources/RxAuthSwift/Base64URL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Foundation

enum Base64URL {
static func encode(_ data: Data) -> String {
data.base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}

static func decode(_ string: String) -> Data? {
var base64 = string
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")

let padding = base64.count % 4
if padding > 0 {
base64.append(String(repeating: "=", count: 4 - padding))
}

return Data(base64Encoded: base64)
}
}
9 changes: 9 additions & 0 deletions Sources/RxAuthSwift/OAuthError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public enum OAuthError: LocalizedError, Sendable {
case noRefreshToken
case invalidCallbackURL
case cancelled
case invalidCredentials
case invalidSignupDetails
case passkeyUnavailable

public var errorDescription: String? {
switch self {
Expand All @@ -34,6 +37,12 @@ public enum OAuthError: LocalizedError, Sendable {
return "Invalid callback URL received"
case .cancelled:
return "Authentication was cancelled"
case .invalidCredentials:
return "Enter a username and password"
case .invalidSignupDetails:
return "Enter a username and password to create an account"
case .passkeyUnavailable:
return "Passkey sign in is not configured for this app"
}
}
}
Expand Down
Loading