Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added SIWA token exchange support #304

Merged
merged 7 commits into from
Sep 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion Auth0/Authentication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ public extension Authentication {
func login(usernameOrEmail username: String, password: String, realm: String, audience: String? = nil, scope: String? = nil, parameters: [String: Any]? = nil) -> Request<Credentials, AuthenticationError> {
return self.login(usernameOrEmail: username, password: password, realm: realm, audience: audience, scope: scope, parameters: parameters)
}

/**
Login using username and password in the default directory

Expand Down Expand Up @@ -895,4 +895,38 @@ public extension Authentication {
func renew(withRefreshToken refreshToken: String, scope: String? = nil) -> Request<Credentials, AuthenticationError> {
return self.renew(withRefreshToken: refreshToken, scope: scope)
}

/**
Authenticate a user with their Sign In With Apple authorization code.

```
Auth0
.authentication(clientId: clientId, domain: "samples.auth0.com")
.tokenExchange(withAppleAuthorizationCode: authCode)
.start { print($0) }
```

and if you need to specify a scope or add additional parameters

```
Auth0
.authentication(clientId: clientId, domain: "samples.auth0.com")
.tokenExchange(withAppleAuthorizationCode: authCode, scope: "openid profile offline_access", audience: "https://myapi.com/api)
.start { print($0) }
```

- parameter authCode: Authorization Code retrieved from Apple Authorization
- parameter scope: requested scope value when authenticating the user. By default is 'openid profile offline_access'
- parameter audience: API Identifier that the client is requesting access to

- returns: a request that will yield Auth0 user's credentials
*/
func tokenExchange(withAppleAuthorizationCode authCode: String, scope: String? = nil, audience: String? = nil) -> Request<Credentials, AuthenticationError> {
var parameters = [ "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": authCode,
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code",
"scope": scope ?? "openid profile offline_access"]
parameters["audience"] = audience
return self.tokenExchange(withParameters: parameters)
}
}
70 changes: 70 additions & 0 deletions Auth0Tests/AuthenticationSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,76 @@ class AuthenticationSpec: QuickSpec {
}

}

// MARK:- Token Exchange

describe("token exchange") {

beforeEach {
stub(condition: isToken(Domain) && hasAtLeast([
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": "VALIDCODE",
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code",
"scope": "openid profile offline_access"
])) { _ in return authResponse(accessToken: AccessToken, idToken: IdToken) }.name = "Token Exchange Apple Success"

stub(condition: isToken(Domain) && hasAtLeast([
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": "VALIDCODE",
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code",
"scope": "openid email"
])) { _ in return authResponse(accessToken: AccessToken, idToken: IdToken) }.name = "Token Exchange Apple Success with custom scope"

stub(condition: isToken(Domain) && hasAtLeast([
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": "VALIDCODE",
"subject_token_type": "http://auth0.com/oauth/token-type/apple-authz-code",
"scope": "openid email",
"audience": "https://myapi.com/api"
])) { _ in return authResponse(accessToken: AccessToken, idToken: IdToken) }.name = "Token Exchange Apple Success with custom scope and audience"
}

it("should exchange apple auth code for credentials") {
waitUntil(timeout: Timeout) { done in
auth.tokenExchange(withAppleAuthorizationCode: "VALIDCODE")
.start { result in
expect(result).to(haveCredentials())
done()
}
}
}

it("should exchange apple auth code and fail") {
waitUntil(timeout: Timeout) { done in
auth.tokenExchange(withAppleAuthorizationCode: "INVALIDCODE")
.start { result in
expect(result).toNot (haveCredentials())
done()
}
}
}

it("should exchange apple auth code for credentials with custom scope") {
waitUntil(timeout: Timeout) { done in
auth.tokenExchange(withAppleAuthorizationCode: "VALIDCODE", scope: "openid email")
.start { result in
expect(result).to(haveCredentials())
done()
}
}
}

it("should exchange apple auth code for credentials with custom scope and audience") {
waitUntil(timeout: Timeout) { done in
auth.tokenExchange(withAppleAuthorizationCode: "VALIDCODE", scope: "openid email", audience: "https://myapi.com/api")
.start { result in
expect(result).to(haveCredentials())
done()
}
}
}

}

describe("revoke refresh token") {

Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,26 @@ You can enable an additional level of user authentication before retrieving cred
credentialsManager.enableBiometrics(withTitle: "Touch to Login")
```

### Sign in With Apple

If you've added [the Sign In with Apple Flow to Your App](https://developer.apple.com/documentation/authenticationservices/adding_the_sign_in_with_apple_flow_to_your_app) you can use the string value from the `authorizationCode` property obtained after a successful Apple authentication to perform a token exchange for Auth0 tokens.

```swift
Auth0
.authentication()
.tokenExchange(withAppleAuthorizationCode: authCode)
.start { result in
switch result {
case .success(let credentials):
print("Obtained credentials: \(credentials)")
case .failure(let error):
print("Failed with \(error)")
}
}
```

Find out more about [Setting up Sign in with Apple](https://auth0.com/docs/connections/apple-setup) with Auth0.

### Authentication API (iOS / macOS / tvOS)

The Authentication API exposes AuthN/AuthZ functionality of Auth0, as well as the supported identity protocols like OpenID Connect, OAuth 2.0, and SAML.
Expand Down