diff --git a/.gitignore b/.gitignore
index 6c0a6fb75..ba762d031 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,4 +40,15 @@ testem.log
Thumbs.db
.nx
-.vscode
\ No newline at end of file
+.vscode
+
+# Created by `dart pub`
+.dart_tool/
+
+# Avoid committing pubspec.lock for library packages; see
+# https://dart.dev/guides/libraries/private-files#pubspeclock.
+pubspec.lock
+
+# env files
+.env*
+!.env.example
diff --git a/README.md b/README.md
index 61ca29466..20dd41c2e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,9 @@
# Affinidi Trust Development Kit (Affinidi TDK)
+
+
[](#contributors-)
+
The Affinidi Trust Development Kit (Affinidi TDK) is a modern interface that allows you to easily manage and integrate [Affinidi Elements](https://www.affinidi.com/product/affinidi-elements) and [Frameworks](https://www.affinidi.com/developer#lota-framework) into your application. It minimises dependencies and enables developers seamless entry into the [Affinidi Trust Network (ATN)](https://www.affinidi.com/get-started).
@@ -17,25 +20,38 @@ Each module has its own README that you can check to better understand how to in
## Available modules
-The Affinidi TDK offers the following modules and support to programming languages:
-
-
-
-| | TypeScript | Python | Dart | PHP |
-|----------- |-----------------------|-----------------------|----------------------|---------------------|
-|**Packages** | | | |
-|[auth-provider](packages/auth-provider/) | 🟢 | 🟢 | 🔴 | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/AuthProvider) |
-|[common](packages/common/) | 🟢 | 🟢 | 🔴 | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Common) |
-|**Packages** | | | |
-|credential-issuance-client | [🟢 Link](clients/typescript/credential-issuance-client/) | [🟢 Link](clients/python/credential_issuance_client/) | [🟡 Link](clients/dart/credential_issuance_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialIssuanceClient) |
-|credential-verification-client | [🟢 Link](clients/typescript/credential-verification-client/) | [🟢 Link](clients/python/credential_verification_client/) | [🟡 Link](clients/dart/credential_verification_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialVerificationClient) |
-|iam-client | [🟢 Link](clients/typescript/iam-client/) | [🟢 Link](clients/python/iam_client/) | [🟡 Link](clients/dart/iam_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IamClient) |
-|iota-client | [🟢 Link](clients/typescript/iota-client/) | [🟢 Link](clients/python/iota_client/) | [🟡 Link](clients/dart/iota_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IotaClient) |
-|login-configuration-client | [🟢 Link](clients/typescript/login-configuration-client/) | [🟢 Link](clients/python/login_configuration_client/) | [🟡 Link](clients/dart/login_configuration_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/LoginConfigurationClient) |
-|wallets-client | [🟢 Link](clients/typescript/wallets-client/) | [🟢 Link](clients/python/wallets_client/) | [🟡 Link](clients/dart/wallets_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/WalletsClient) |
-|**Libraries** | | | |
-|[iota-browser](libs/iota-browser/) | 🟢 | 🔴 | 🔴 | 🔴 |
-|[iota-core](libs/iota-core/) | 🟢 | 🟢 | 🔴 | 🔴 |
+The Affinidi TDK offers several modules depending on the type of application you are using and the programming language.
+
+### For vault applications
+
+If you are building a vault application that manages user's data, you will be interested in the following packages:
+
+| | TypeScript | Dart |
+| ------------------------- | -------------------------------------------------------- | -------------------------------------------------- |
+| **Packages** | | |
+| consumer-auth-provider | 🔴 | [🟡 Link](packages/dart/consumer_auth_provider/) |
+| **Clients** | | |
+| vault-data-manager-client | [🟡 Link](clients/typescript/vault-data-manager-client/) | [🟡 Link](clients/dart/vault_data_manager_client/) |
+
+### For issuer/verifier applications
+
+If you are building a site that issues or requests data from the user vaults you will be interested in the following packages:
+
+| | TypeScript | Python | Dart | PHP |
+| ------------------------------ | ------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
+| **Packages** | | | |
+| auth-provider | [🟢 Link](packages/jsii/auth-provider/) | 🟢 | [🟡 Link](packages/dart/auth_provider/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/AuthProvider) |
+| common | [🟢 Link](packages/jsii/common/) | 🟢 | 🔴 | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Common) |
+| **Clients** | | | |
+| credential-issuance-client | [🟢 Link](clients/typescript/credential-issuance-client/) | [🟢 Link](clients/python/credential_issuance_client/) | [🟡 Link](clients/dart/credential_issuance_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialIssuanceClient) |
+| credential-verification-client | [🟢 Link](clients/typescript/credential-verification-client/) | [🟢 Link](clients/python/credential_verification_client/) | [🟡 Link](clients/dart/credential_verification_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialVerificationClient) |
+| iam-client | [🟢 Link](clients/typescript/iam-client/) | [🟢 Link](clients/python/iam_client/) | [🟡 Link](clients/dart/iam_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IamClient) |
+| iota-client | [🟢 Link](clients/typescript/iota-client/) | [🟢 Link](clients/python/iota_client/) | [🟡 Link](clients/dart/iota_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IotaClient) |
+| login-configuration-client | [🟢 Link](clients/typescript/login-configuration-client/) | [🟢 Link](clients/python/login_configuration_client/) | [🟡 Link](clients/dart/login_configuration_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/LoginConfigurationClient) |
+| wallets-client | [🟢 Link](clients/typescript/wallets-client/) | [🟢 Link](clients/python/wallets_client/) | [🟡 Link](clients/dart/wallets_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/WalletsClient) |
+| **Libraries** | | | |
+| iota-browser | [🟢 Link](libs/iota-browser/) | 🔴 | 🔴 | 🔴 |
+| iota-core | [🟢 Link](libs/iota-core/) | 🟢 | 🔴 | 🔴 |
🟢 Supported
diff --git a/packages/dart/.gitignore b/packages/dart/.gitignore
new file mode 100644
index 000000000..4a33ddae8
--- /dev/null
+++ b/packages/dart/.gitignore
@@ -0,0 +1 @@
+.fvmrc
\ No newline at end of file
diff --git a/packages/dart/auth_provider/.env.example b/packages/dart/auth_provider/.env.example
new file mode 100644
index 000000000..fdbc65d24
--- /dev/null
+++ b/packages/dart/auth_provider/.env.example
@@ -0,0 +1,10 @@
+# Personal access token
+PROJECT_ID=""
+TOKEN_ID=""
+PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
+PASSPHRASE="" # Optional. Required if private key is encrypted
+KEY_ID="" # Optional. Required if token's key id is different from token id
+
+# Iota (Websocket)
+IOTA_CONFIG_ID=""
+DID="" # Usually obtained at runtime from the registered user
\ No newline at end of file
diff --git a/packages/dart/auth_provider/.gitignore b/packages/dart/auth_provider/.gitignore
new file mode 100644
index 000000000..5fe01895a
--- /dev/null
+++ b/packages/dart/auth_provider/.gitignore
@@ -0,0 +1,11 @@
+# https://dart.dev/guides/libraries/private-files
+# Created by `dart pub`
+.dart_tool/
+
+# Avoid committing pubspec.lock for library packages; see
+# https://dart.dev/guides/libraries/private-files#pubspeclock.
+pubspec.lock
+
+# env files
+.env*
+!.env.example
diff --git a/packages/dart/auth_provider/CHANGELOG.md b/packages/dart/auth_provider/CHANGELOG.md
new file mode 100644
index 000000000..effe43c82
--- /dev/null
+++ b/packages/dart/auth_provider/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0
+
+- Initial version.
diff --git a/packages/dart/auth_provider/README.md b/packages/dart/auth_provider/README.md
new file mode 100644
index 000000000..8b55e735b
--- /dev/null
+++ b/packages/dart/auth_provider/README.md
@@ -0,0 +1,39 @@
+
+
+TODO: Put a short description of the package here that helps potential users
+know whether this package might be useful for them.
+
+## Features
+
+TODO: List what your package can do. Maybe include images, gifs, or videos.
+
+## Getting started
+
+TODO: List prerequisites and provide or point to information on how to
+start using the package.
+
+## Usage
+
+TODO: Include short and useful examples for package users. Add longer examples
+to `/example` folder.
+
+```dart
+const like = 'sample';
+```
+
+## Additional information
+
+TODO: Tell users more about the package: where to find more information, how to
+contribute to the package, how to file issues, what response they can expect
+from the package authors, and more.
diff --git a/packages/dart/auth_provider/analysis_options.yaml b/packages/dart/auth_provider/analysis_options.yaml
new file mode 100644
index 000000000..dee8927aa
--- /dev/null
+++ b/packages/dart/auth_provider/analysis_options.yaml
@@ -0,0 +1,30 @@
+# This file configures the static analysis results for your project (errors,
+# warnings, and lints).
+#
+# This enables the 'recommended' set of lints from `package:lints`.
+# This set helps identify many issues that may lead to problems when running
+# or consuming Dart code, and enforces writing Dart using a single, idiomatic
+# style and format.
+#
+# If you want a smaller set of lints you can change this to specify
+# 'package:lints/core.yaml'. These are just the most critical lints
+# (the recommended set includes the core lints).
+# The core lints are also what is used by pub.dev for scoring packages.
+
+include: package:lints/recommended.yaml
+
+# Uncomment the following section to specify additional rules.
+
+# linter:
+# rules:
+# - camel_case_types
+
+# analyzer:
+# exclude:
+# - path/to/excluded/files/**
+
+# For more information about the core and recommended set of lints, see
+# https://dart.dev/go/core-lints
+
+# For additional information about configuring this file, see
+# https://dart.dev/guides/language/analysis-options
diff --git a/packages/dart/auth_provider/example/iota_token.dart b/packages/dart/auth_provider/example/iota_token.dart
new file mode 100644
index 000000000..8d3263da2
--- /dev/null
+++ b/packages/dart/auth_provider/example/iota_token.dart
@@ -0,0 +1,33 @@
+import 'package:affinidi_tdk_auth_provider/affinidi_tdk_auth_provider.dart';
+import 'package:dotenv/dotenv.dart';
+
+void main() async {
+ var env = DotEnv()..load();
+ if (!env.isEveryDefined(
+ ['PROJECT_ID', 'TOKEN_ID', 'PRIVATE_KEY', 'IOTA_CONFIG_ID', 'DID'])) {
+ print(
+ 'Missing environment variables. Please provide PROJECT_ID, TOKEN_ID, PRIVATE_KEY, DID');
+ return;
+ }
+ // Workaround for dotenv multiline limitations
+ final privateKey = env['PRIVATE_KEY']!.replaceAll('\\n', '\n');
+
+ final provider = AuthProvider(
+ projectId: env['PROJECT_ID']!,
+ tokenId: env['TOKEN_ID']!,
+ privateKey: privateKey,
+ // Optional parameters
+ keyId: env['KEY_ID'],
+ passphrase: env['PASSPHRASE'],
+ );
+
+ try {
+ // Fetch iota token (websocket). Did is usually obtained at runtime from the registered user
+ final iotaToken = provider.createIotaToken(
+ iotaConfigId: env['IOTA_CONFIG_ID']!, did: env['DID']!);
+ print('Successfully obtained iota token:');
+ print(iotaToken);
+ } catch (e) {
+ print('Error obtaining token: $e');
+ }
+}
diff --git a/packages/dart/auth_provider/example/project_scoped_token.dart b/packages/dart/auth_provider/example/project_scoped_token.dart
new file mode 100644
index 000000000..151799d1c
--- /dev/null
+++ b/packages/dart/auth_provider/example/project_scoped_token.dart
@@ -0,0 +1,31 @@
+import 'package:affinidi_tdk_auth_provider/affinidi_tdk_auth_provider.dart';
+import 'package:dotenv/dotenv.dart';
+
+void main() async {
+ final env = DotEnv(includePlatformEnvironment: true)..load();
+ if (!env.isEveryDefined(['PROJECT_ID', 'TOKEN_ID', 'PRIVATE_KEY'])) {
+ print(
+ 'Missing environment variables. Please provide PROJECT_ID, TOKEN_ID, PRIVATE_KEY');
+ return;
+ }
+ // Workaround for dotenv multiline limitations
+ final privateKey = env['PRIVATE_KEY']!.replaceAll('\\n', '\n');
+
+ final provider = AuthProvider(
+ projectId: env['PROJECT_ID']!,
+ tokenId: env['TOKEN_ID']!,
+ privateKey: privateKey,
+ // Optional parameters
+ keyId: env['KEY_ID'],
+ passphrase: env['PASSPHRASE'],
+ );
+
+ try {
+ // Fetch project scoped token
+ final projectScopedToken = await provider.fetchProjectScopedToken();
+ print('Successfully obtained project scoped token:');
+ print(projectScopedToken);
+ } catch (e) {
+ print('Error obtaining token: $e');
+ }
+}
diff --git a/packages/dart/auth_provider/lib/affinidi_tdk_auth_provider.dart b/packages/dart/auth_provider/lib/affinidi_tdk_auth_provider.dart
new file mode 100644
index 000000000..d761bb676
--- /dev/null
+++ b/packages/dart/auth_provider/lib/affinidi_tdk_auth_provider.dart
@@ -0,0 +1,8 @@
+/// Support for doing something awesome.
+///
+/// More dartdocs go here.
+library;
+
+export 'src/auth_provider.dart';
+
+// TODO: Export any libraries intended for clients of this package.
diff --git a/packages/dart/auth_provider/lib/src/auth_provider.dart b/packages/dart/auth_provider/lib/src/auth_provider.dart
new file mode 100644
index 000000000..1f58209c8
--- /dev/null
+++ b/packages/dart/auth_provider/lib/src/auth_provider.dart
@@ -0,0 +1,141 @@
+import 'dart:convert';
+
+import 'package:affinidi_tdk_common/affinidi_tdk_common.dart';
+import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
+import 'package:http/http.dart' as http;
+import 'package:uuid/uuid.dart';
+
+import 'iam_client.dart';
+import 'jwt_helper.dart';
+
+class AuthProvider {
+ final String projectId;
+ final String tokenId;
+ final String privateKey;
+ final String? keyId;
+ final String? passphrase;
+ final String apiGatewayUrl;
+ final String tokenEndpoint;
+
+ AuthProvider({
+ required this.projectId,
+ required this.tokenId,
+ required this.privateKey,
+ this.keyId,
+ this.passphrase,
+ }) : apiGatewayUrl = Environment.fetchApiGwUrl(),
+ tokenEndpoint = Environment.fetchElementsAuthTokenUrl();
+
+ AuthProvider.withEnv({
+ required this.projectId,
+ required this.tokenId,
+ required this.privateKey,
+ this.keyId,
+ this.passphrase,
+ required this.apiGatewayUrl,
+ required this.tokenEndpoint,
+ });
+
+ ECPublicKey? _publicKey;
+ String? _projectScopedToken;
+
+ Future _shouldFetchToken() async {
+ if (_projectScopedToken == null) {
+ return true;
+ }
+
+ final iamClient = IamClient(apiGatewayUrl: apiGatewayUrl);
+ _publicKey ??= await JWTHelper.fetchPublicKey(iamClient);
+ try {
+ JWT.verify(_projectScopedToken!, _publicKey!);
+ return false;
+ } on JWTException {
+ return true;
+ }
+ }
+
+ Future fetchProjectScopedToken() async {
+ if (await _shouldFetchToken()) {
+ _projectScopedToken =
+ await _getProjectScopedToken(audience: tokenEndpoint);
+ }
+ return _projectScopedToken!;
+ }
+
+ Future _getUserAccessToken({required String audience}) async {
+ final token = JWTHelper.signPayload(
+ audience: audience,
+ tokenId: tokenId,
+ privateKey: privateKey,
+ keyId: keyId,
+ passphrase: passphrase,
+ );
+
+ final response = await http.post(
+ Uri.parse(audience),
+ headers: {'Content-Type': 'application/x-www-form-urlencoded'},
+ body: {
+ 'grant_type': 'client_credentials',
+ 'scope': 'openid',
+ 'client_assertion_type':
+ 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
+ 'client_assertion': token,
+ 'client_id': tokenId,
+ },
+ );
+
+ if (response.statusCode != 200) {
+ throw Exception('Failed to get user access token');
+ }
+
+ final data = jsonDecode(response.body);
+ return data['access_token'];
+ }
+
+ Future _getProjectScopedToken({
+ required String audience,
+ }) async {
+ final userAccessToken = await _getUserAccessToken(audience: audience);
+
+ final response = await http.post(
+ Uri.parse('$apiGatewayUrl/iam/v1/sts/create-project-scoped-token'),
+ headers: {
+ 'Authorization': 'Bearer $userAccessToken',
+ 'Content-Type': 'application/json',
+ },
+ body: jsonEncode({'projectId': projectId}),
+ );
+
+ if (response.statusCode != 200) {
+ throw Exception('Failed to get project scoped token');
+ }
+
+ final data = jsonDecode(response.body);
+ return data['accessToken'];
+ }
+
+ ({String iotaJwt, String iotaSessionId}) createIotaToken({
+ required String iotaConfigId,
+ required String did,
+ String? iotaSessionId,
+ }) {
+ final sessionId = iotaSessionId ?? Uuid().v4();
+
+ return (
+ iotaJwt: JWTHelper.signPayload(
+ audience: did,
+ tokenId: 'token/$tokenId',
+ privateKey: privateKey,
+ keyId: keyId,
+ passphrase: passphrase,
+ additionalPayload: {
+ 'project_id': projectId,
+ 'iota_configuration_id': iotaConfigId,
+ 'iota_session_id': sessionId,
+ 'scope': 'iota_channel',
+ },
+ ),
+ iotaSessionId: sessionId,
+ );
+ }
+}
diff --git a/packages/dart/auth_provider/lib/src/iam_client.dart b/packages/dart/auth_provider/lib/src/iam_client.dart
new file mode 100644
index 000000000..8f452b731
--- /dev/null
+++ b/packages/dart/auth_provider/lib/src/iam_client.dart
@@ -0,0 +1,33 @@
+import 'dart:convert';
+
+import 'package:http/http.dart' as http;
+
+class IamClient {
+ IamClient({
+ required String apiGatewayUrl,
+ http.Client? httpClient,
+ }) : _httpClient = httpClient ?? http.Client(),
+ _apiGatewayUrl = apiGatewayUrl;
+
+ final http.Client _httpClient;
+ final String _apiGatewayUrl;
+
+ Future