Skip to content
Open
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
41 changes: 41 additions & 0 deletions auth0_flutter/EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [Passwordless Login](#passwordless-login)
- [Retrieve user information](#retrieve-user-information)
- [Renew credentials](#renew-credentials)
- [Custom Token Exchange](#custom-token-exchange)
- [Errors](#errors-2)
- [πŸŒπŸ“± Organizations](#-organizations)
- [Log in to an organization](#log-in-to-an-organization)
Expand Down Expand Up @@ -700,6 +701,46 @@ final didStore =

> πŸ’‘ To obtain a refresh token, make sure your Auth0 application has the **refresh token** [grant enabled](https://auth0.com/docs/get-started/applications/update-grant-types). If you are also specifying an audience value, make sure that the corresponding Auth0 API has the **Allow Offline Access** [setting enabled](https://auth0.com/docs/get-started/apis/api-settings#access-settings).

### Custom Token Exchange

[Custom Token Exchange](https://auth0.com/docs/authenticate/custom-token-exchange) allows you to enable applications to exchange their existing tokens for Auth0 tokens when calling the /oauth/token endpoint. This is useful for advanced integration use cases, such as:
- Integrate an external identity provider
- Migrate to Auth0

> **Note:** This feature is currently available in [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access). Please reach out to Auth0 support to enable it for your tenant.

<details>
<summary>Mobile (Android/iOS)</summary>

```dart
final credentials = await auth0.api.customTokenExchange(
subjectToken: 'external-idp-token',
subjectTokenType: 'urn:acme:legacy-token',
audience: 'https://api.example.com', // Optional
scopes: {'openid', 'profile', 'email'}, // Optional, defaults to {'openid', 'profile', 'email'}
organization: 'org_abc123', // Optional
);
```

</details>

<details>
<summary>Web</summary>

```dart
final credentials = await auth0Web.customTokenExchange(
subjectToken: 'external-idp-token',
subjectTokenType: 'urn:acme:legacy-token',
audience: 'https://api.example.com', // Optional
scopes: {'openid', 'profile', 'email'}, // Optional
organizationId: 'org_abc123', // Optional
);
```

</details>

> πŸ’‘ For more information, see the [Custom Token Exchange documentation](https://auth0.com/docs/authenticate/custom-token-exchange) and [RFC 8693](https://tools.ietf.org/html/rfc8693).

### Errors

The Authentication API client will only throw `ApiException` exceptions. You can find more information in the `details` property of the exception. Check the [API documentation](https://pub.dev/documentation/auth0_flutter_platform_interface/latest/auth0_flutter_platform_interface/ApiException-class.html) to learn more about the available `ApiException` properties.
Expand Down
2 changes: 1 addition & 1 deletion auth0_flutter/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ android {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'com.auth0.android:auth0:3.11.0'
implementation 'com.auth0.android:auth0:3.12.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.hamcrest:java-hamcrest:2.0.0.0'
testImplementation "org.mockito.kotlin:mockito-kotlin:4.1.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class Auth0FlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
SignupApiRequestHandler(),
UserInfoApiRequestHandler(),
RenewApiRequestHandler(),
CustomTokenExchangeApiRequestHandler(),
ResetPasswordApiRequestHandler()
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.auth0.auth0_flutter.request_handlers.api

import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.android.authentication.AuthenticationException
import com.auth0.android.callback.Callback
import com.auth0.android.result.Credentials
import com.auth0.auth0_flutter.request_handlers.MethodCallRequest
import com.auth0.auth0_flutter.toMap
import com.auth0.auth0_flutter.utils.assertHasProperties
import io.flutter.plugin.common.MethodChannel
import java.util.ArrayList

private const val AUTH_CUSTOM_TOKEN_EXCHANGE_METHOD = "auth#customTokenExchange"

class CustomTokenExchangeApiRequestHandler : ApiRequestHandler {
override val method: String = AUTH_CUSTOM_TOKEN_EXCHANGE_METHOD

override fun handle(
api: AuthenticationAPIClient,
request: MethodCallRequest,
result: MethodChannel.Result
) {
val args = request.data
assertHasProperties(listOf("subjectToken", "subjectTokenType"), args)

val organization = if (args["organization"] is String) args["organization"] as String else null

val builder = api.customTokenExchange(
args["subjectTokenType"] as String,
args["subjectToken"] as String,
organization
).apply {
val scopes = (args["scopes"] ?: arrayListOf<String>()) as ArrayList<*>
if (scopes.isNotEmpty()) {
setScope(scopes.joinToString(separator = " "))
}
if (args["audience"] is String) {
setAudience(args["audience"] as String)
}
validateClaims()
}

builder.start(object : Callback<Credentials, AuthenticationException> {
override fun onFailure(exception: AuthenticationException) {
result.error(
exception.getCode(),
exception.getDescription(),
exception.toMap()
)
}

override fun onSuccess(credentials: Credentials) {
val scope = credentials.scope?.split(" ") ?: listOf()
val formattedDate = credentials.expiresAt.toInstant().toString()
result.success(
mapOf(
"accessToken" to credentials.accessToken,
"idToken" to credentials.idToken,
"refreshToken" to credentials.refreshToken,
"userProfile" to credentials.user.toMap(),
"expiresAt" to formattedDate,
"scopes" to scope,
"tokenType" to credentials.type
)
)
}
})
}
}
Loading
Loading