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

Add support for API key provider #919

Merged
merged 25 commits into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
* Added support for realm list of nullable primitive types, ie. `RealmList<int?>`. ([#163](https://github.com/realm/realm-dart/issues/163))
* Allow null arguments on query. ([#871](https://github.com/realm/realm-dart/issues/871))

* Added support for API key authentication. (Issue [#432](https://github.com/realm/realm-dart/issues/432))
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
* Expose `User.apiKeys` client - this client can be used to create, fetch, and delete API keys.
* Expose `Credentials.apiKey` that enable authentication with API keys.
* Exposed `User.accessToken` and `User.refreshToken` - these tokens can be used to authenticate against the server when calling HTTP API outside of the Dart/Flutter SDK. For example, if you want to use the GraphQL or the data access API. (PR [#919](https://github.com/realm/realm-dart/pull/919))

### Fixed
* Previously removeAt did not truncate length. ([#883](https://github.com/realm/realm-dart/issues/883))
* List.length= now throws, if you try to increase length. This previously succeeded silently. ([#894](https://github.com/realm/realm-dart/pull/894)).
Expand Down
18 changes: 18 additions & 0 deletions lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,22 @@ extension AppInternal on App {
AppHandle get handle => _handle;

static App create(AppHandle handle) => App._(handle);

static AppException createException(String message, String? linkToLogs, int statusCode) => AppException._(message, linkToLogs, statusCode);
}

/// An exception thrown from operations interacting with a Atlas App Services app.
class AppException extends RealmException {
/// A link to the server logs associated with this exception if available.
final String? linkToServerLogs;

/// The HTTP status code returned by the server for this exception.
final int statusCode;

AppException._(super.message, this.linkToServerLogs, this.statusCode);

@override
String toString() {
return "AppException: $message, link to server logs: $linkToServerLogs";
}
}
15 changes: 13 additions & 2 deletions lib/src/cli/atlas_apps/baas_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ class BaasClient {
"runResetFunction": true
}''');

await enableProvider(app, 'api-key');

if (publicRSAKey.isNotEmpty) {
String publicRSAKeyEncoded = jsonEncode(publicRSAKey);
final dynamic createSecretResult = await _post('groups/$_groupId/apps/$appId/secrets', '{"name":"rsPublicKey","value":$publicRSAKeyEncoded}');
Expand Down Expand Up @@ -247,7 +249,7 @@ class BaasClient {
"authFunctionName": "authFunc",
"authFunctionId": "$authFuncId"
}''');

const facebookSecret = "876750ac6d06618b323dee591602897f";
final dynamic createFacebookSecretResult = await _post('groups/$_groupId/apps/$appId/secrets', '{"name":"facebookSecret","value":"$facebookSecret"}');
String facebookClientSecretKeyName = createFacebookSecretResult['name'] as String;
Expand Down Expand Up @@ -292,7 +294,7 @@ class BaasClient {
"name": "picture"
}''');
}

print('Creating database db_$name$_appSuffix');

await _createMongoDBService(app, '''{
Expand Down Expand Up @@ -353,6 +355,15 @@ class BaasClient {
}
}

Future<String> createApiKey(BaasApp app, String name, bool enabled) async {
final dynamic result = await _post('groups/$_groupId/apps/${app.appId}/api_keys', '{ "name":"$name" }');
if (!enabled) {
await _put('groups/$_groupId/apps/${app.appId}/api_keys/${result['_id']}/disable', '');
}

return result['key'] as String;
}

Future<void> _authenticate(String provider, String credentials) async {
dynamic response = await _post('auth/providers/$provider/login', credentials);

Expand Down
43 changes: 33 additions & 10 deletions lib/src/credentials.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,54 @@ import 'dart:ffi';

import 'native/realm_core.dart';
import 'app.dart';
import 'user.dart';

/// An enum containing all authentication providers. These have to be enabled manually for the application before they can be used.
/// [Authentication Providers Docs](https://docs.mongodb.com/realm/authentication/providers/)
/// {@category Application}
enum AuthProviderType {
/// For authenticating without credentials.
anonymous,
anonymous(0),

/// For authenticating without credentials using a new anonymous user.
anonymousNoReuse,
anonymousNoReuse(1),

/// Authenticate with Apple Id
apple,
apple(2),

/// Authenticate with Facebook account.
facebook,
facebook(3),

/// Authenticate with Google account
google,
google(4),

/// For authenticating with JSON web token.
jwt,
jwt(5),

/// For authenticating with an email and a password.
emailPassword,
emailPassword(6),

/// For authenticating with custom function with payload argument.
function,
function(7),

_userApiKey,
_serverApiKey
/// For authenticating with an API key.
apiKey(8);

const AuthProviderType(this._value);

final int _value;
}

extension AuthProviderTypeInternal on AuthProviderType {
static AuthProviderType getByValue(int value) {
for (final type in AuthProviderType.values) {
if (type._value == value) {
return type;
}
}

throw ArgumentError('Invalid AuthProviderType value: $value');
}
}

/// A class, representing the credentials used for authenticating a [User]
Expand Down Expand Up @@ -106,6 +123,12 @@ class Credentials implements Finalizable {
Credentials.function(String payload)
: _handle = realmCore.createAppCredentialsFunction(payload),
provider = AuthProviderType.function;

/// Returns a [Credentials] object that can be used to authenticate a user with an API key.
/// To generate an API key, use [ApiKeyClient.create] or the App Services web UI.
Credentials.apiKey(String key)
: _handle = realmCore.createAppCredentialsApiKey(key),
provider = AuthProviderType.apiKey;
}

/// @nodoc
Expand Down
Loading