Skip to content

Commit 5e6a256

Browse files
asmcleancocojoe
authored andcommitted
Add support for extended profile using SIWA token exchange (#322)
* Add support for extended profile using SIWA token exchange * Fix indenting/whitespace * Revert accidental Cartfile.resolved changes * Reorder SIWA parameters, and stop sending missing values * Expand test cases for SIWA
1 parent 97952b2 commit 5e6a256

File tree

2 files changed

+95
-7
lines changed

2 files changed

+95
-7
lines changed

Auth0/Authentication.swift

+24-6
Original file line numberDiff line numberDiff line change
@@ -911,21 +911,39 @@ public extension Authentication {
911911
```
912912
Auth0
913913
.authentication(clientId: clientId, domain: "samples.auth0.com")
914-
.tokenExchange(withAppleAuthorizationCode: authCode, scope: "openid profile offline_access", audience: "https://myapi.com/api)
914+
.tokenExchange(withAppleAuthorizationCode: authCode, scope: "openid profile offline_access", audience: "https://myapi.com/api, fullName: credentials.fullName)
915915
.start { print($0) }
916916
```
917917

918918
- parameter authCode: Authorization Code retrieved from Apple Authorization
919919
- parameter scope: requested scope value when authenticating the user. By default is 'openid profile offline_access'
920920
- parameter audience: API Identifier that the client is requesting access to
921+
- parameter fullName: The full name property returned with the Apple ID Credentials
921922

922923
- returns: a request that will yield Auth0 user's credentials
923924
*/
924-
func tokenExchange(withAppleAuthorizationCode authCode: String, scope: String? = nil, audience: String? = nil) -> Request<Credentials, AuthenticationError> {
925-
var parameters = [ "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
926-
"subject_token": authCode,
927-
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code",
928-
"scope": scope ?? "openid profile offline_access"]
925+
func tokenExchange(withAppleAuthorizationCode authCode: String, scope: String? = nil, audience: String? = nil, fullName: PersonNameComponents? = nil) -> Request<Credentials, AuthenticationError> {
926+
var parameters: [String: String] =
927+
[ "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
928+
"subject_token": authCode,
929+
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code",
930+
"scope": scope ?? "openid profile offline_access" ]
931+
if let fullName = fullName {
932+
let name = [
933+
"firstName": fullName.givenName,
934+
"lastName": fullName.familyName
935+
].filter { $0.value != nil }.mapValues { $0! }
936+
if !name.isEmpty, let jsonData = try? String(
937+
data: JSONSerialization.data(
938+
withJSONObject: [
939+
"name": name
940+
],
941+
options: []),
942+
encoding: String.Encoding.utf8
943+
) {
944+
parameters["user_profile"] = jsonData
945+
}
946+
}
929947
parameters["audience"] = audience
930948
return self.tokenExchange(withParameters: parameters)
931949
}

Auth0Tests/AuthenticationSpec.swift

+71-1
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,28 @@ class AuthenticationSpec: QuickSpec {
242242
"scope": "openid email",
243243
"audience": "https://myapi.com/api"
244244
])) { _ in return authResponse(accessToken: AccessToken, idToken: IdToken) }.name = "Token Exchange Apple Success with custom scope and audience"
245+
246+
stub(condition: isToken(Domain) && hasAtLeast([
247+
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
248+
"subject_token": "VALIDNAMECODE",
249+
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code"]) &&
250+
(hasAtLeast(["user_profile": "{\"name\":{\"lastName\":\"Smith\",\"firstName\":\"John\"}}" ]) || hasAtLeast(["user_profile": "{\"name\":{\"firstName\":\"John\",\"lastName\":\"Smith\"}}" ]))
251+
) { _ in return authResponse(accessToken: AccessToken, idToken: IdToken) }.name = "Token Exchange Apple Success with user profile"
252+
253+
stub(condition: isToken(Domain) && hasAtLeast([
254+
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
255+
"subject_token": "VALIDPARTIALNAMECODE",
256+
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code",
257+
"user_profile": "{\"name\":{\"firstName\":\"John\"}}"
258+
])) { _ in return authResponse(accessToken: AccessToken, idToken: IdToken) }.name = "Token Exchange Apple Success with partial user profile"
259+
260+
stub(condition: isToken(Domain) && hasAtLeast([
261+
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
262+
"subject_token": "VALIDMISSINGNAMECODE",
263+
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code"]) &&
264+
hasNoneOf(["user_profile"])
265+
) { _ in return authResponse(accessToken: AccessToken, idToken: IdToken) }.name = "Token Exchange Apple Success with missing user profile"
266+
245267
}
246268

247269
it("should exchange apple auth code for credentials") {
@@ -284,8 +306,56 @@ class AuthenticationSpec: QuickSpec {
284306
}
285307
}
286308

287-
}
309+
it("should exchange apple auth code for credentials with fullName") {
310+
var fullName = PersonNameComponents()
311+
fullName.givenName = "John"
312+
fullName.familyName = "Smith"
313+
fullName.middleName = "Ignored"
288314

315+
waitUntil(timeout: Timeout) { done in
316+
auth.tokenExchange(withAppleAuthorizationCode: "VALIDNAMECODE",
317+
fullName: fullName)
318+
.start { result in
319+
expect(result).to(haveCredentials())
320+
done()
321+
}
322+
}
323+
}
324+
325+
it("should exchange apple auth code for credentials with partial fullName") {
326+
var fullName = PersonNameComponents()
327+
fullName.givenName = "John"
328+
fullName.familyName = nil
329+
fullName.middleName = "Ignored"
330+
331+
waitUntil(timeout: Timeout) { done in
332+
auth.tokenExchange(withAppleAuthorizationCode: "VALIDPARTIALNAMECODE",
333+
fullName: fullName)
334+
.start { result in
335+
expect(result).to(haveCredentials())
336+
done()
337+
}
338+
}
339+
}
340+
341+
it("should exchange apple auth code for credentials with missing fullName") {
342+
var fullName = PersonNameComponents()
343+
fullName.givenName = nil
344+
fullName.familyName = nil
345+
fullName.middleName = nil
346+
347+
waitUntil(timeout: Timeout) { done in
348+
auth.tokenExchange(withAppleAuthorizationCode: "VALIDMISSINGNAMECODE",
349+
fullName: fullName)
350+
.start { result in
351+
expect(result).to(haveCredentials())
352+
done()
353+
}
354+
}
355+
}
356+
357+
}
358+
289359
describe("revoke refresh token") {
290360

291361
let refreshToken = UUID().uuidString.replacingOccurrences(of: "-", with: "")

0 commit comments

Comments
 (0)