Skip to content

Commit 9a21f6f

Browse files
Merge pull request #21 from michaelbabiy/as/principals/ab#1444015-pkce-support-and-token-refresh-error
MB: Add PKCE Support and Distinguish Bad Requests in Token Refresh Error Handling (AB#1444015)
2 parents 352b5eb + d0c8b5c commit 9a21f6f

File tree

5 files changed

+22
-17
lines changed

5 files changed

+22
-17
lines changed

Framework/AtomNetworking/Atom/Extensions/Foundation/URLSession+Extensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ extension URLSession {
4242
// - Authorization Failure: Posts `Atom.didFailToAuthorizeRequest` notification.
4343
//
4444
// Both notifications include the error in `userInfo`.
45-
if error.isAccessTokenRefreshFailure {
45+
if requestable.isAuthenticationEndpoint, error.isBadRequest {
4646
// Notify observers that a token refresh has failed.
4747
NotificationCenter.default.post(name: Atom.didFailToRefreshAccessToken, object: nil, userInfo: ["error": error])
4848
} else if error.isAuthorizationFailure {

Framework/AtomNetworking/Atom/Extensions/Framework/AtomError+Extensions.swift

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,12 @@ extension AtomError {
2828
return response.statusCode == 401
2929
}
3030

31-
/// Returns `Bool` indicating whether the error is due to a failed attempt to refresh an access token.
31+
/// Returns `Bool` indicating whether the error is a general "Bad Request" (HTTP 400) from a normal API response.
3232
///
33-
/// A client might encouter this error if the authorization server determines that the
34-
/// refresh token used in a request is invalid or expired.
35-
///
36-
/// Unlike `AtomError` case - `.response` - token refresh error is nested inside of the `.session` error
37-
/// case to make differentiation between a standard `Bad Request` and `Bad Request` due to an invalid /
38-
/// or expired access token easier.
39-
public var isAccessTokenRefreshFailure: Bool {
40-
guard
41-
case let .session(error) = self,
42-
case let .response(response) = error as? AtomError
43-
else {
33+
/// This detects cases where an API endpoint returned a 400 status code, typically due to invalid parameters,
34+
/// malformed requests, or other client errors.
35+
public var isBadRequest: Bool {
36+
guard case let .response(response) = self else {
4437
return false
4538
}
4639

Framework/AtomNetworking/Atom/Extensions/Framework/AuthenticationEndpoint+Extensions.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,13 @@ extension AuthenticationEndpoint: Requestable {
5858
var method: HTTPMethod {
5959
let grantType = Identifier.grantType.appending(credential.grantType.rawValue)
6060
let clientID = Identifier.clientID.appending(credential.id)
61-
let clientSecret = Identifier.clientSecret.appending(credential.secret)
6261
let refreshToken = Identifier.refreshToken.appending(writable.tokenCredential.refreshToken)
63-
let bodyString = grantType + "&" + clientID + "&" + clientSecret + "&" + refreshToken
62+
var bodyString = grantType + "&" + clientID + "&" + refreshToken
63+
64+
if let secret = credential.secret {
65+
bodyString.append("&")
66+
bodyString.append(Identifier.clientSecret.appending(secret))
67+
}
6468

6569
return .post(Data(bodyString.utf8))
6670
}

Framework/AtomNetworking/Atom/Extensions/Framework/Requestable+Extensions.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@
1717
import Foundation
1818

1919
extension Requestable {
20+
/// Returns a `Bool` indicating whether the current requestable is targeted at an authentication endpoint.
21+
///
22+
/// This property is `true` when the conforming type is `AuthenticationEndpoint`. It allows Atom to quickly identify
23+
/// whether the request involves authentication-related operations, such as token refresh.
24+
var isAuthenticationEndpoint: Bool {
25+
self is AuthenticationEndpoint
26+
}
27+
2028
/// Applies authorization header to the `Requestable` instance.
2129
///
2230
/// - Parameters:

Framework/AtomNetworking/Atom/Models/ClientCredential.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public struct ClientCredential: Sendable, Equatable {
2929
public let id: String
3030

3131
/// The client secret. The client MAY omit the parameter if the client secret is an empty string. See RFC 6749.
32-
public let secret: String
32+
public let secret: String?
3333

3434
// MARK: - Lifecycle
3535

@@ -39,7 +39,7 @@ public struct ClientCredential: Sendable, Equatable {
3939
/// - grantType: The authorization grant type as described in Sections 4.1.3, 4.3.2, 4.4.2, RFC 6749.
4040
/// - id: The client identifier issued to the client during the registration process described by Section 2.2, RFC 6749.
4141
/// - secret: The client secret. The client MAY omit the parameter if the client secret is an empty string. See RFC 6749.
42-
public init(grantType: GrantType = .refreshToken, id: String, secret: String) {
42+
public init(grantType: GrantType = .refreshToken, id: String, secret: String? = nil) {
4343
self.id = id
4444
self.grantType = grantType
4545
self.secret = secret

0 commit comments

Comments
 (0)