Repository navigation
Breaking changes
- APUserAgentGenerator 2.0. The dependency is now pinned with
from: "2.0.0"instead ofbranch: "main", so APWebAuthentication itself
can be installed with a version requirement, and DeviceKit is no longer
pulled in. GenerateduserAgentstrings change accordingly:.default/.ios/.iphone/nil: Mobile Safari on the current iOS,
which WebKit reports with the frozeniPhone OS 18_7token..android: real Chrome for Android, i.e. the reduced
Linux; Android 10; Ktoken and the currentChrome/<major>.0.0.0
(was a Pixel 7 with Chrome 123)..iosChrome,.desktop,.desktopFirefox: current Chrome / Firefox
versions with vendor-accurate reduced tokens.WebAuthViewController.usesBrowserUserAgentreports the running device's
real iOS version (IOSDevice.current), as before.
- The minimum deployment target is iOS 17.4.
- The TikTok web client (
TikTokWeb*,AccountType.tiktok) was removed (#17). OAuth2Interceptornow sends the access token in theAuthorizationheader by default.
The defaulttokenLocationchanged from.paramsto.authorizationHeader
(RFC 6750 §2.1; query-string tokens leak into logs and caches). Every built-in
provider already passes its location explicitly, so only custom interceptors
created withouttokenLocation:are affected. To keep the old behaviour, pass
tokenLocation: .params(Foursquare's interceptor does).AuthClientonly attaches credentials (bearer, DPoP, CSRF, OAuth 1.0a) to
requests whose host is the host ofbaseURLString. Requests to absolute URLs on
other hosts are sent unauthenticated and never trigger a token refresh. List
extra hosts inadditionalAuthorizedHosts. Subclasses that override
makeSessionManager(configuration:)should compose
hostRestrictedRequestInterceptorinstead ofrequestInterceptor.- AT Protocol OAuth callbacks must carry
iss, and it must equal the
authorization server's issuer exactly (RFC 9207).stateis checked before an
error=response is acted on. APWebAuthSession.start()throws if anotherstart()is still pending.
WebTokenInterceptorViewController.start()does the same, and calling it after
the flow finished reports the original error instead of returning silently.- TLS failures (
.secureConnectionFailed, server certificate errors) are no
longer classified as ignorable connection errors.
Added
-
Bluesky / AT Protocol. OAuth with PAR, PKCE and DPoP
(ATProtoOAuthClient),ATProtoAuthentication,ATProtoClient,
BlueskyAPIClientandBlueskyUser. -
Mastodon.
MastodonAuthentication(persists the server host with the
token),MastodonAPIClient(per-server base URL, host normalization,
authorize URL),MastodonUserandMastodonRelationship. -
WebAuthViewController:usesBrowserUserAgent(present the web view as
Mobile Safari; x.com's login link does nothing for the in-app user agent),
links targeting a new window load in place, callback URLs are picked out of
failed app-scheme redirects, and ashouldReturnToAuthURLhook bounces a
flow back to the auth URL when login lands elsewhere (capped at three
reloads). -
OAuth 2.0 authorization code flow with PKCE.
OAuth2AuthorizationCodeFlow
builds the authorization URL (response_type=code, randomstate,S256
challenge), runs it inASWebAuthenticationSessionorAPWebAuthSession,
validates the redirect andstatebefore readingerror=, exchanges the
code withcode_verifierandredirect_uriusing any
OAuth2ClientAuthenticationmode, and stores the tokens with
Auth2Authentication.applyTokenResponse(_:). Each step is also public for
custom UI. Presets:.x(...),.gitHub(...),.mastodon(host:...). -
PKCE: verifier generation andS256challenge (RFC 7636), now shared with
the AT Protocol client (no behaviour change there). -
Proactive OAuth 2.0 refresh.
Auth2Authentication.expiresAtrecords when
the access token expires. It is set fromexpires_inon refresh, and on first
login via the newapplyTokenResponse(_:). It is persisted in the Keychain
blob; blobs saved by older versions still load, withexpiresAt=nil.
OAuth2Interceptorrefreshes before sending a request when the token
expires withinrefreshLeeway(default 60 s). Concurrent requests share one
in-flight refresh, which the 401 retry path now uses too. On failure the
current token is sent and proactive attempts pause for
proactiveRefreshBackoff(default 30 s). The clock is injectable (now:). -
Token refresh responses accept
expires_inas a number or a numeric string.
A refresh response withoutexpires_inclearsexpiresAtrather than
keeping the old token's expiry. -
Token refresh for Reddit (
https://www.reddit.com/api/v1/access_token),
Pinterest v5 (https://api.pinterest.com/v5/oauth/token) and Twitch
(https://id.twitch.tv/oauth2/token). -
OAuth2ClientAuthentication.basicAuthorizationHeaderAllowingEmptySecretfor
token endpoints that require HTTP Basic even from public clients (Reddit
installed apps:client_id:with an empty password). -
TransientNetworkRetrierhonoursRetry-After(seconds or HTTP-date, capped).
Fixed
- OAuth 2 refresh asks for JSON (
Accept: application/json) and only wipes
stored tokens oninvalid_grant. APWebAuthSession.start()andWebAuthViewControllerresume the caller with
.canceledwhen the controller is dismissed programmatically or released,
and never resume it twice.- OAuth 1.0a signatures no longer include URL fragment parameters, and a
request without a usable base URI fails withsignatureGenerationFailed
instead of producing an invalid signature. - AT Protocol entryway logins only store the DID document's handle after it
resolves back to the same DID. JSON.int32Numberno longer traps on values outside theInt32range.TransientNetworkRetrierno longer retries501 Not Implemented.
Tests and CI
- WebTokenInterceptor and OAuth 1.0a signature vector tests, an iOS 17.4
minimum-version simulator lane, and a SwiftLint lane (#22). - Tests assert the APUserAgentGenerator 2.0 user agents for every browser mode.