Skip to content

Commit fdd771f

Browse files
authored
Merge branch 'master' into par-poc
2 parents 740258c + a113ec3 commit fdd771f

40 files changed

Lines changed: 661 additions & 139 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ playground.xcworkspace
4444
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
4545
# Packages/
4646
# Package.pins
47-
# Package.resolved
47+
Package.resolved
4848
.build/
4949
.swiftpm/
5050

OktaAuthFoundation.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "OktaAuthFoundation"
33
s.module_name = "AuthFoundation"
4-
s.version = "2.1.4"
4+
s.version = "2.1.5"
55
s.summary = "Okta Authentication Foundation"
66
s.description = <<-DESC
77
Provides the foundation and common features used to authenticate users, managing the lifecycle and storage of tokens and credentials, and provide a base for other Okta SDKs to build upon.

OktaBrowserSignin.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "OktaBrowserSignin"
33
s.module_name = "BrowserSignin"
4-
s.version = "2.1.4"
4+
s.version = "2.1.5"
55
s.summary = "Okta Browser Sign In UI"
66
s.description = <<-DESC
77
Authenticate users using web-based OIDC.

OktaClient.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "OktaClient"
3-
s.version = "2.1.4"
3+
s.version = "2.1.5"
44
s.summary = "Secure client authentication, request authorization, and user management capabilities for Swift."
55
s.description = <<-DESC
66
Provides a modularized set of libraries that provide the building blocks and convenience features used to authenticate users, manage the lifecycle and storage of tokens and user credentials, and provide a base for other libraries and applications to build upon.

OktaDirectAuth.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "OktaDirectAuth"
3-
s.version = "2.1.4"
3+
s.version = "2.1.5"
44
s.summary = "Okta Direct Authentication"
55
s.description = <<-DESC
66
Enables application developers to build native sign in experiences using the Okta Direct Authentication API.

OktaIdxAuth.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'OktaIdxAuth'
3-
s.version = '2.1.4'
3+
s.version = '2.1.5'
44
s.summary = 'SDK to easily integrate the Okta Identity Engine'
55
s.description = <<-DESC
66
Integrate your native app with Okta using the Okta Identity Engine library.

OktaOAuth2.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "OktaOAuth2"
33
s.module_name = "OAuth2Auth"
4-
s.version = "2.1.4"
4+
s.version = "2.1.5"
55
s.summary = "Okta OAuth2 Authentication"
66
s.description = <<-DESC
77
Enables application developers to authenticate users utilizing a variety of OAuth2 authentication flows.

README.md

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,53 @@ pod 'OktaIdxAuth'
182182

183183
## Usage Guide
184184

185+
Every authentication flow follows the same two-method pattern defined by the
186+
`AuthenticationFlow` protocol:
187+
188+
1. **`start()`** — initiates the flow (builds an authorize URL, sends
189+
credentials, etc.) and returns either a token or an intermediate value
190+
needed by the next step.
191+
2. **`resume()`** _(optional)_ — completes a multi-step flow by exchanging the
192+
intermediate value for tokens (e.g., trading an authorization code for an
193+
access token, polling for device authorization, etc.).
194+
195+
Single-step flows such as Resource Owner, JWT Bearer, and Token Exchange
196+
resolve entirely in `start()`. Multi-step flows like Authorization Code and
197+
Device Authorization require a subsequent call to `resume()`.
198+
199+
All flows accept an optional **authentication context** that carries
200+
cross-cutting parameters such as `audience`, `resource`, `nonce`, and
201+
`maxAge`. See [Authentication Context](#authentication-context) below for
202+
details.
203+
204+
### Authentication Context
205+
206+
All authentication flows accept an optional `context` parameter that carries cross-cutting authentication properties. Some flows require a specific context type (e.g., `AuthorizationCodeFlow.Context` for the Authorization Code flow), while simpler flows use `StandardAuthenticationContext`:
207+
208+
```swift
209+
let flow = ResourceOwnerFlow(issuerURL: URL(string: "https://example.okta.com")!,
210+
clientId: "abc123client",
211+
scope: "openid offline_access email profile")
212+
let token = try await flow.start(
213+
username: "jane@example.com",
214+
password: "secretPassword",
215+
context: .init(
216+
audience: "api://my-resource-server",
217+
resource: "https://api.example.com/v1", // or an array of URIs
218+
maxAge: 3600,
219+
nonce: "custom-nonce"
220+
)))
221+
```
222+
223+
| Property | Type | Description |
224+
| --- | --- | --- |
225+
| `nonce` | `String?` | Custom nonce for ID token replay protection. |
226+
| `maxAge` | `TimeInterval?` | Maximum authentication age (seconds). |
227+
| `audience` | `String?` | Target resource server ([RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)). Sent in the token request. |
228+
| `resource` | `[String]?` | Target resource URI(s) ([RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)). Sent in the token request. Accepts a string literal or an array of strings. |
229+
| `acrValues` | `[String]?` | Requested Authentication Context Class Reference values. |
230+
| `additionalParameters` | `[String: String]?` | Extra parameters forwarded to the token request. |
231+
185232
### Web Authentication using OIDC
186233

187234
The simplest way to integrate authentication in your app is with OIDC through a web browser, using the Authorization Code Flow grant.
@@ -263,8 +310,7 @@ When using the `device_sso` scope, your application can receive a "device secret
263310
let flow = TokenExchangeFlow(
264311
issuerURL: URL(string: "https://example.okta.com")!,
265312
clientId: "abc123client",
266-
scope: "openid offline_access email profile",
267-
audience: .default)
313+
scope: "openid offline_access email profile")
268314

269315
let token = try await flow.start(with: [
270316
.actor(type: .deviceSecret, value: "DeviceToken"),
@@ -317,7 +363,7 @@ let flow = try InteractionCodeFlow(issuerURL: URL(string: "https://example.okta.
317363
```
318364

319365
For more information, see the [OktaIdxAuth API documentation][oktaidxauth-docs].
320-
366+
321367
## Storing and using tokens
322368

323369
Once your user has authenticated and you have a `Token` object, your application can store and use those credentials. The most direct approach is to use the `Credential.store(_:tags:security:)` function.

Samples/AuthenticationFlows.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ func signInUsingDeviceSSO(deviceToken: String, idToken: String) async throws {
9393
// Create the flow
9494
let flow = TokenExchangeFlow(issuerURL: issuerUrl,
9595
clientId: clientId,
96-
scope: "openid profile offline_access",
97-
audience: .default)
96+
scope: "openid profile offline_access")
9897

9998
// Exchange the ID and Device tokens for access tokens.
10099
let token = try await flow.start(with: [

Sources/AuthFoundation/Migration/SDKVersion.swift

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,18 @@ import Foundation
1616
import CommonSupport
1717
#endif
1818

19-
#if canImport(UIKit)
20-
import UIKit
21-
#endif
22-
23-
#if os(watchOS)
24-
import WatchKit
25-
#endif
26-
2719
#if canImport(Android)
2820
import Android
2921
#endif
3022

3123
private let deviceModel: String = {
3224
var system = utsname()
3325
uname(&system)
34-
let model = withUnsafePointer(to: &system.machine.0) { ptr in
26+
let model = withUnsafeBytes(of: &system.machine) { buf in
27+
guard let ptr = buf.baseAddress?.assumingMemoryBound(to: CChar.self)
28+
else {
29+
return "unknown"
30+
}
3531
return String(cString: ptr)
3632
}
3733
return model
@@ -52,22 +48,16 @@ private let systemName: String = {
5248
return "android"
5349
#elseif os(Linux)
5450
return "linux"
55-
#elseif os(Android)
56-
return "android"
51+
#elseif os(Windows)
52+
return "windows"
53+
#else
54+
return "unknown"
5755
#endif
5856
}()
5957

6058
private let systemVersion: String = {
61-
#if os(iOS) || os(tvOS) || (swift(>=5.10) && os(visionOS))
62-
return MainActor.nonisolatedUnsafe {
63-
UIDevice.current.systemVersion
64-
}
65-
#elseif os(watchOS)
66-
return WKInterfaceDevice.current().systemVersion
67-
#else
68-
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
69-
return "\(osVersion.majorVersion).\(osVersion.minorVersion).\(osVersion.patchVersion)"
70-
#endif
59+
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
60+
return "\(osVersion.majorVersion).\(osVersion.minorVersion).\(osVersion.patchVersion)"
7161
}()
7262

7363
/// Utility class that allows SDK components to register their name and version for use in HTTP User-Agent values.
@@ -101,47 +91,56 @@ public struct SDKVersion: Sendable {
10191
@discardableResult
10292
public static func register(sdk: SDKVersion) -> SDKVersion {
10393
lock.withLock {
104-
guard _sdkVersions.filter({ $0.name == sdk.name }).isEmpty else {
105-
return sdk
106-
}
107-
108-
_sdkVersions.append(sdk)
109-
110-
let sdkVersionString = _sdkVersions
111-
.sorted(by: { $0.name.rawValue < $1.name.rawValue })
112-
.map(\.displayName)
113-
.joined(separator: " ")
114-
_userAgent = "\(sdkVersionString) \(systemName)/\(systemVersion) Device/\(deviceModel)"
94+
_register(sdk: sdk)
95+
}
96+
}
11597

98+
private static func _register(sdk: SDKVersion) -> SDKVersion {
99+
guard _sdkVersions.filter({ $0.name == sdk.name }).isEmpty else {
116100
return sdk
117101
}
102+
103+
_sdkVersions.append(sdk)
104+
105+
let sdkVersionString = _sdkVersions
106+
.sorted(by: { $0.name.rawValue < $1.name.rawValue })
107+
.map(\.displayName)
108+
.joined(separator: " ")
109+
_userAgent = "\(sdkVersionString) \(systemName)/\(systemVersion) Device/\(deviceModel)"
110+
111+
return sdk
118112
}
119113

120114
/// Convenience function used to register an SDK
121115
/// - Parameters:
122116
/// - name: SDK name.
123117
/// - versionString: SDK version.
124118
/// - Returns: The resulting SDKVersion object.
125-
@inlinable
126119
@discardableResult
127120
public static func register(_ name: Name, version versionString: String) -> SDKVersion? {
128-
let sdk = version(for: name) ?? register(sdk: SDKVersion(sdk: name, version: versionString))
129-
guard sdk.version == versionString
130-
else {
131-
return nil
121+
lock.withLock {
122+
let sdk = _version(for: name) ?? _register(sdk: SDKVersion(sdk: name, version: versionString))
123+
guard sdk.version == versionString
124+
else {
125+
return nil
126+
}
127+
return sdk
132128
}
133-
return sdk
134129
}
135130

136131
/// Returns the version information for the given SDK.
137132
/// - Parameter sdkName: SDK name to search for.
138133
/// - Returns: Version information for the given SDK name.
139134
public static func version(for sdkName: Name) -> SDKVersion? {
140135
lock.withLock {
141-
_sdkVersions.first(where: { $0.name == sdkName })
136+
_version(for: sdkName)
142137
}
143138
}
144139

140+
private static func _version(for sdkName: Name) -> SDKVersion? {
141+
_sdkVersions.first(where: { $0.name == sdkName })
142+
}
143+
145144
// MARK: Private properties / methods
146145
private static let lock = Lock()
147146
nonisolated(unsafe) private static var _sdkVersions: [SDKVersion] = []

0 commit comments

Comments
 (0)