Intermediate · 14 min
Sign in with OAuth 2.0
PKCE, the token exchange, and one refresh shared by every caller.
You’ll buildA client that attaches a token, refreshes it once, and asks for a sign-in when the session really is over.
1
Step 1
Start the flow
Keep the verifier and the state. The callback is worthless without them, and the state check is what stops someone else's authorization code being redeemed in your user's session.
let pkce = PKCEChallenge.generate()
let state = UUID().uuidString
let url = try oauth.authorizationURL(state: state, challenge: pkce)
// The app opens `url`: presenting a browser needs a window
// anchor, which is the app's job, not the library's.2
Step 2
Validate what comes back
The state is checked before anything else is read, and a mismatch throws without returning the code.
let code = try oauth.authorizationCode(
from: callbackURL,
expectedState: state
)
let token = try await oauth.exchange(code: code, verifier: pkce.verifier)
try await authenticator.setToken(token)3
Step 3
Wire it into the client
The client already coordinates a single refresh across a burst of 401s and replays them. This fills in the closure it takes.
var configuration = NetworkClientConfiguration()
configuration.authRefreshProvider = authenticator.refreshProvider
configuration.middleware = [authenticator.middleware]
let client = NetworkClient(configuration: configuration)4
Step 4
Let eight callers need a token at once
An actor is not enough on its own: it suspends at the network call, so a second caller would start a second refresh. Sharing the in-flight task means the provider sees one request.
await withTaskGroup(of: Void.self) { group in
for _ in 0..<8 {
group.addTask { _ = try? await authenticator.validToken() }
}
}
// The provider saw exactly one refresh.Tutorial complete