Skip to content

Commit 1304fe9

Browse files
authored
Org support for custom token exchange (#1035)
Organization support for custom token exchange
1 parent 911fa92 commit 1304fe9

File tree

5 files changed

+106
-29
lines changed

5 files changed

+106
-29
lines changed

Auth0/Auth0Authentication.swift

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,11 @@ struct Auth0Authentication: Authentication {
162162
telemetry: self.telemetry)
163163
}
164164

165-
func login(appleAuthorizationCode authorizationCode: String, fullName: PersonNameComponents?, profile: [String: Any]?, audience: String?, scope: String) -> Request<Credentials, AuthenticationError> {
165+
func login(appleAuthorizationCode authorizationCode: String,
166+
fullName: PersonNameComponents?,
167+
profile: [String: Any]?,
168+
audience: String?,
169+
scope: String) -> Request<Credentials, AuthenticationError> {
166170
var parameters: [String: Any] = [:]
167171
var profile: [String: Any] = profile ?? [:]
168172

@@ -185,7 +189,10 @@ struct Auth0Authentication: Authentication {
185189
parameters: parameters)
186190
}
187191

188-
func login(facebookSessionAccessToken sessionAccessToken: String, profile: [String: Any], audience: String?, scope: String) -> Request<Credentials, AuthenticationError> {
192+
func login(facebookSessionAccessToken sessionAccessToken: String,
193+
profile: [String: Any],
194+
audience: String?,
195+
scope: String) -> Request<Credentials, AuthenticationError> {
189196
var parameters: [String: String] = [:]
190197
if let jsonData = try? JSONSerialization.data(withJSONObject: profile, options: []),
191198
let json = String(data: jsonData, encoding: .utf8) {
@@ -499,11 +506,16 @@ struct Auth0Authentication: Authentication {
499506
telemetry: self.telemetry)
500507
}
501508

502-
func customTokenExchange(subjectToken: String, subjectTokenType: String, audience: String?, scope: String) -> Request<Credentials, AuthenticationError> {
509+
func customTokenExchange(subjectToken: String,
510+
subjectTokenType: String,
511+
audience: String?,
512+
scope: String,
513+
organization: String?) -> Request<Credentials, AuthenticationError> {
503514
return self.tokenExchange(subjectToken: subjectToken,
504515
subjectTokenType: subjectTokenType,
505516
scope: scope,
506-
audience: audience)
517+
audience: audience,
518+
organization: organization)
507519
}
508520

509521
}
@@ -535,14 +547,20 @@ private extension Auth0Authentication {
535547
dpop: self.dpop)
536548
}
537549

538-
func tokenExchange(subjectToken: String, subjectTokenType: String, scope: String, audience: String?, parameters: [String: Any] = [:]) -> Request<Credentials, AuthenticationError> {
550+
func tokenExchange(subjectToken: String,
551+
subjectTokenType: String,
552+
scope: String,
553+
audience: String?,
554+
organization: String? = nil,
555+
parameters: [String: Any] = [:]) -> Request<Credentials, AuthenticationError> {
539556
var parameters: [String: Any] = parameters
540557
parameters["client_id"] = self.clientId
541558
parameters["grant_type"] = "urn:ietf:params:oauth:grant-type:token-exchange"
542559
parameters["subject_token"] = subjectToken
543560
parameters["subject_token_type"] = subjectTokenType
544561
parameters["audience"] = audience
545562
parameters["scope"] = includeRequiredScope(in: scope)
563+
parameters["organization"] = organization
546564

547565
let token = URL(string: "oauth/token", relativeTo: self.url)!
548566
return Request(session: session,

Auth0/Authentication.swift

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,11 @@ public protocol Authentication: SenderConstraining, Trackable, Loggable {
341341

342342
- [Authentication API Endpoint](https://auth0.com/docs/api/authentication/token-exchange-for-native-social/token-exchange-native-social)
343343
*/
344-
func login(appleAuthorizationCode authorizationCode: String, fullName: PersonNameComponents?, profile: [String: Any]?, audience: String?, scope: String) -> Request<Credentials, AuthenticationError>
344+
func login(appleAuthorizationCode authorizationCode: String,
345+
fullName: PersonNameComponents?,
346+
profile: [String: Any]?,
347+
audience: String?,
348+
scope: String) -> Request<Credentials, AuthenticationError>
345349

346350
/**
347351
Logs a user in with their Facebook [session info access token](https://developers.facebook.com/docs/facebook-login/access-tokens/session-info-access-token/) and profile data.
@@ -386,7 +390,10 @@ public protocol Authentication: SenderConstraining, Trackable, Loggable {
386390

387391
- [Authentication API Endpoint](https://auth0.com/docs/api/authentication/token-exchange-for-native-social/token-exchange-native-social)
388392
*/
389-
func login(facebookSessionAccessToken sessionAccessToken: String, profile: [String: Any], audience: String?, scope: String) -> Request<Credentials, AuthenticationError>
393+
func login(facebookSessionAccessToken sessionAccessToken: String,
394+
profile: [String: Any],
395+
audience: String?,
396+
scope: String) -> Request<Credentials, AuthenticationError>
390397

391398
/**
392399
Logs a user in using a username and password in the default directory.
@@ -1113,7 +1120,7 @@ public protocol Authentication: SenderConstraining, Trackable, Loggable {
11131120
}
11141121
```
11151122

1116-
You can also include additional parameters:
1123+
You can also include organization parameter:
11171124

11181125
```swift
11191126
Auth0
@@ -1122,7 +1129,7 @@ public protocol Authentication: SenderConstraining, Trackable, Loggable {
11221129
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
11231130
audience: "https://example.com/api",
11241131
scope: "openid profile email",
1125-
additionalParameters: ["custom_claim": "value"])
1132+
organization: "org_id")
11261133
.start { print($0) }
11271134
```
11281135

@@ -1131,17 +1138,20 @@ public protocol Authentication: SenderConstraining, Trackable, Loggable {
11311138
- subjectTokenType: URI that identifies the type of the subject token.
11321139
- audience: API Identifier that your application is requesting access to. Defaults to `nil`.
11331140
- scope: Space-separated list of requested scope values. Defaults to `openid profile email`.
1134-
- additionalParameters: Additional parameters to include in the token exchange request. Defaults to empty dictionary.
1141+
- organization: Identifier of an organization the user is a member of.
11351142
- Returns: A request that will yield Auth0 user's credentials.
1136-
1143+
11371144
## See Also
11381145

11391146
- [Authentication API Endpoint](https://auth0.com/docs/api/authentication/token-exchange)
11401147
- [Custom Token Exchange Documentation](https://auth0.com/docs/authenticate/custom-token-exchange)
11411148
- [RFC 8693: OAuth 2.0 Token Exchange](https://tools.ietf.org/html/rfc8693)
11421149
*/
1143-
func customTokenExchange(subjectToken: String, subjectTokenType: String, audience: String?, scope: String) -> Request<Credentials, AuthenticationError>
1144-
1150+
func customTokenExchange(subjectToken: String,
1151+
subjectTokenType: String,
1152+
audience: String?,
1153+
scope: String,
1154+
organization: String?) -> Request<Credentials, AuthenticationError>
11451155
}
11461156

11471157
public extension Authentication {
@@ -1166,12 +1176,26 @@ public extension Authentication {
11661176
return self.multifactorChallenge(mfaToken: mfaToken, types: types, authenticatorId: authenticatorId)
11671177
}
11681178

1169-
func login(appleAuthorizationCode authorizationCode: String, fullName: PersonNameComponents? = nil, profile: [String: Any]? = nil, audience: String? = nil, scope: String = defaultScope) -> Request<Credentials, AuthenticationError> {
1170-
return self.login(appleAuthorizationCode: authorizationCode, fullName: fullName, profile: profile, audience: audience, scope: scope)
1179+
func login(appleAuthorizationCode authorizationCode: String,
1180+
fullName: PersonNameComponents? = nil,
1181+
profile: [String: Any]? = nil,
1182+
audience: String? = nil,
1183+
scope: String = defaultScope) -> Request<Credentials, AuthenticationError> {
1184+
return self.login(appleAuthorizationCode: authorizationCode,
1185+
fullName: fullName,
1186+
profile: profile,
1187+
audience: audience,
1188+
scope: scope)
11711189
}
11721190

1173-
func login(facebookSessionAccessToken sessionAccessToken: String, profile: [String: Any], audience: String? = nil, scope: String = defaultScope) -> Request<Credentials, AuthenticationError> {
1174-
return self.login(facebookSessionAccessToken: sessionAccessToken, profile: profile, audience: audience, scope: scope)
1191+
func login(facebookSessionAccessToken sessionAccessToken: String,
1192+
profile: [String: Any],
1193+
audience: String? = nil,
1194+
scope: String = defaultScope) -> Request<Credentials, AuthenticationError> {
1195+
return self.login(facebookSessionAccessToken: sessionAccessToken,
1196+
profile: profile,
1197+
audience: audience,
1198+
scope: scope)
11751199
}
11761200

11771201
func loginDefaultDirectory(withUsername username: String, password: String, audience: String? = nil, scope: String = defaultScope) -> Request<Credentials, AuthenticationError> {
@@ -1251,8 +1275,16 @@ public extension Authentication {
12511275
return self.renew(withRefreshToken: refreshToken, audience: audience, scope: scope)
12521276
}
12531277

1254-
func customTokenExchange(subjectToken: String, subjectTokenType: String, audience: String? = nil, scope: String = defaultScope) -> Request<Credentials, AuthenticationError> {
1255-
return self.customTokenExchange(subjectToken: subjectToken, subjectTokenType: subjectTokenType, audience: audience, scope: scope)
1278+
func customTokenExchange(subjectToken: String,
1279+
subjectTokenType: String,
1280+
audience: String? = nil,
1281+
scope: String = defaultScope,
1282+
organization: String? = nil) -> Request<Credentials, AuthenticationError> {
1283+
return self.customTokenExchange(subjectToken: subjectToken,
1284+
subjectTokenType: subjectTokenType,
1285+
audience: audience,
1286+
scope: scope,
1287+
organization: organization)
12561288
}
12571289

12581290
}

Auth0/MyAccount/AuthenticationMethods/MyAccountAuthenticationMethods.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ public protocol MyAccountAuthenticationMethods: MyAccountClient {
217217
///
218218
/// - Returns: A request that will yield a totp enrollment challenge
219219
func enrollTOTP() -> Request<TOTPEnrollmentChallenge, MyAccountError>
220-
220+
221221
/// Enrolls a new ToTP authentication method. This is the last part of the enrollment flow.
222222
///
223223
/// ## Availability

Auth0Tests/AuthenticationSpec.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,7 @@ class AuthenticationSpec: QuickSpec {
19281928
describe("customTokenExchange") {
19291929
let subjectToken = "example-token"
19301930
let subjectTokenType = "urn:ietf:params:oauth:token-type:jwt"
1931+
let orgId = "org_id"
19311932

19321933
it("should exchange custom token for credentials") {
19331934
NetworkStub.addStub(condition: {
@@ -1974,6 +1975,29 @@ class AuthenticationSpec: QuickSpec {
19741975
}
19751976
}
19761977

1978+
it("should exchange custom token with organization") {
1979+
NetworkStub.addStub(condition: {
1980+
$0.isToken(Domain) && $0.hasAllOf([
1981+
"grant_type": TokenExchangeGrantType,
1982+
"subject_token": subjectToken,
1983+
"subject_token_type": subjectTokenType,
1984+
"scope": defaultScope,
1985+
"organization": orgId,
1986+
"client_id": ClientId
1987+
])
1988+
}, response: authResponse(accessToken: AccessToken, idToken: IdToken))
1989+
1990+
waitUntil(timeout: Timeout) { done in
1991+
auth.customTokenExchange(subjectToken: subjectToken,
1992+
subjectTokenType: subjectTokenType,
1993+
organization: orgId)
1994+
.start { result in
1995+
expect(result).to(haveCredentials(AccessToken, IdToken))
1996+
done()
1997+
}
1998+
}
1999+
}
2000+
19772001
it("should produce authentication error") {
19782002
let code = "invalid_grant"
19792003
let description = "Invalid subject token"

EXAMPLES.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3215,9 +3215,10 @@ Auth0
32153215
Auth0
32163216
.authentication()
32173217
.customTokenExchange(subjectToken: "existing-token",
3218-
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
3219-
audience: "https://example.com/api",
3220-
scope: "openid profile email")
3218+
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
3219+
audience: "https://example.com/api",
3220+
scope: "openid profile email",
3221+
organization: "org_id)
32213222
.start { result in
32223223
switch result {
32233224
case .success(let credentials):
@@ -3235,9 +3236,10 @@ do {
32353236
let credentials = try await Auth0
32363237
.authentication()
32373238
.customTokenExchange(subjectToken: "existing-token",
3238-
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
3239-
audience: "https://example.com/api",
3240-
scope: "openid profile email")
3239+
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
3240+
audience: "https://example.com/api",
3241+
scope: "openid profile email",
3242+
organization: "org_id")
32413243
.start()
32423244
print("Obtained credentials: \(credentials)")
32433245
} catch {
@@ -3253,9 +3255,10 @@ do {
32533255
Auth0
32543256
.authentication()
32553257
.customTokenExchange(subjectToken: "existing-token",
3256-
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
3257-
audience: "https://example.com/api",
3258-
scope: "openid profile email")
3258+
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
3259+
audience: "https://example.com/api",
3260+
scope: "openid profile email",
3261+
organization: "org_id")
32593262
.start()
32603263
.sink(receiveCompletion: { completion in
32613264
if case .failure(let error) = completion {

0 commit comments

Comments
 (0)