Skip to content

Commit

Permalink
[firebase_auth] Added Oauth for generic provider for iOS and Android …
Browse files Browse the repository at this point in the history
…devices (firebase#1526)

Add support for OAuth Authentication for iOS and Android to solve generic providers authentication.
  • Loading branch information
zariweyo authored and collinjackson committed Dec 17, 2019
1 parent bcd503a commit 34ec274
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 8 deletions.
4 changes: 4 additions & 0 deletions packages/firebase_auth/firebase_auth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.15.2+1

* Add support for OAuth Authentication for iOS and Android to solve generic providers authentication.

## 0.15.2

* Add web support by default.
Expand Down
4 changes: 2 additions & 2 deletions packages/firebase_auth/firebase_auth/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ android {
}
dependencies {
implementation 'androidx.annotation:annotation:1.0.0'
implementation 'com.google.firebase:firebase-common:16.1.0'
api 'com.google.firebase:firebase-auth:17.0.0'
implementation 'com.google.firebase:firebase-common:19.3.0'
api 'com.google.firebase:firebase-auth:19.2.0'
api 'com.google.code.gson:gson:2.8.5'
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.google.firebase.auth.GetTokenResult;
import com.google.firebase.auth.GithubAuthProvider;
import com.google.firebase.auth.GoogleAuthProvider;
import com.google.firebase.auth.OAuthProvider;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.auth.PhoneAuthProvider.ForceResendingToken;
Expand Down Expand Up @@ -461,7 +462,32 @@ private AuthCredential getCredential(Map<String, Object> arguments) {
}
default:
{
credential = null;
String providerId = (String) arguments.get("provider");
String idToken = data.get("idToken");
String accessToken = data.get("accessToken");
String rawNonce = data.get("rawNonce");

if (providerId != null && providerId != "" && idToken != null && idToken != "") {
OAuthProvider.CredentialBuilder oAuthProvider =
OAuthProvider.newCredentialBuilder(providerId);

if (accessToken != null && accessToken != "" && rawNonce != null && rawNonce != "") {
oAuthProvider.setAccessToken(accessToken);
oAuthProvider.setIdTokenWithRawNonce(idToken, rawNonce);
credential = oAuthProvider.build();
} else if (accessToken != null && accessToken != "") {
oAuthProvider.setAccessToken(accessToken);
oAuthProvider.setIdToken(idToken);
credential = oAuthProvider.build();
} else if (rawNonce != null && rawNonce != "") {
oAuthProvider.setIdTokenWithRawNonce(idToken, rawNonce);
credential = oAuthProvider.build();
} else {
credential = null;
}
} else {
credential = null;
}
break;
}
}
Expand Down
37 changes: 37 additions & 0 deletions packages/firebase_auth/firebase_auth/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/

# Web related
lib/generated_plugin_registrant.dart

# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
android.useAndroidX=true
android.enableJetifier=true
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="io.flutter.plugins.firebaseauthexample">

<uses-permission android:name="android.permission.INTERNET"/>

<application android:name="io.flutter.app.FlutterApplication" android:label="firebase_auth_example" android:icon="@mipmap/ic_launcher">
<application
android:name="io.flutter.app.FlutterApplication"
android:label="firebase_auth_example"
android:icon="@mipmap/ic_launcher">
<activity android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Black.NoTitleBar"
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,32 @@ - (FIRAuthCredential *)getCredential:(NSDictionary *)arguments {
credential = [[FIRPhoneAuthProvider providerWithAuth:[self getAuth:arguments]]
credentialWithVerificationID:verificationId
verificationCode:smsCode];
} else if ([provider length] != 0 && data[@"idToken"] != (id)[NSNull null] &&
(data[@"accessToken"] != (id)[NSNull null] ||
data[@"rawNonce"] != (id)[NSNull null])) {
NSString *idToken = data[@"idToken"];
NSString *accessToken = data[@"accessToken"];
NSString *rawNonce = data[@"rawNonce"];

if (accessToken != (id)[NSNull null] && rawNonce != (id)[NSNull null] &&
[accessToken length] != 0 && [rawNonce length] != 0) {
credential = [FIROAuthProvider credentialWithProviderID:provider
IDToken:idToken
rawNonce:rawNonce
accessToken:accessToken];
} else if (accessToken != (id)[NSNull null] && [accessToken length] != 0) {
credential = [FIROAuthProvider credentialWithProviderID:provider
IDToken:idToken
accessToken:accessToken];
} else if (rawNonce != (id)[NSNull null] && [rawNonce length] != 0) {
credential = [FIROAuthProvider credentialWithProviderID:provider
IDToken:idToken
rawNonce:rawNonce];
} else {
NSLog(@"To use OAuthProvider you need to provide at least one of the following 'accessToken' "
@"or 'rawNonce'.");
}

} else {
NSLog(@"Support for an auth provider with identifier '%@' is not implemented.", provider);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Firebase Auth plugin for Flutter.
s.public_header_files = 'Classes/**/*.h'
s.ios.deployment_target = '8.0'
s.dependency 'Flutter'
s.dependency 'Firebase/Auth', '~> 6.0'
s.dependency 'Firebase/Auth', '~> 6.3'
s.dependency 'Firebase/Core'
s.static_framework = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ part 'src/auth_provider/github_auth_provider.dart';
part 'src/auth_provider/google_auth_provider.dart';
part 'src/auth_provider/phone_auth_provider.dart';
part 'src/auth_provider/twitter_auth_provider.dart';
part 'src/auth_provider/oauth_auth_provider.dart';
part 'src/additional_user_info.dart';
part 'src/auth_result.dart';
part 'src/firebase_auth.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

part of firebase_auth;

class OAuthProvider {
const OAuthProvider({@required this.providerId}) : assert(providerId != null);

/// The provider ID with which this provider is associated
final String providerId;

/// Creates an [OAuthCredential] for the OAuth 2 provider with the provided parameters.
OAuthCredential getCredential({
@required String idToken,
String accessToken,
String rawNonce,
}) {
return PlatformOAuthCredential(
providerId: providerId,
idToken: idToken,
accessToken: accessToken,
rawNonce: rawNonce);
}
}
4 changes: 2 additions & 2 deletions packages/firebase_auth/firebase_auth/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for Firebase Auth, enabling Android and iOS
authentication using passwords, phone numbers and identity providers
like Google, Facebook and Twitter.
homepage: https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_auth/firebase_auth
version: 0.15.2
version: 0.15.2+1

flutter:
plugin:
Expand All @@ -19,7 +19,7 @@ flutter:
dependencies:
meta: ^1.0.4
firebase_core: ^0.4.0
firebase_auth_platform_interface: ^1.0.0
firebase_auth_platform_interface: ^1.1.1
# The design on https://flutter.dev/go/federated-plugins was to leave
# this constraint as "any". We cannot do it right now as it fails pub publish
# validation, so we set a ^ constraint.
Expand Down
64 changes: 64 additions & 0 deletions packages/firebase_auth/firebase_auth/test/firebase_auth_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,23 @@ void main() {
expect(captured.accessToken, equals(kMockAccessToken));
});

test('OAuthProvider signInWithCredential for Apple', () async {
OAuthProvider oAuthProvider = OAuthProvider(providerId: "apple.com");
final AuthCredential credential = oAuthProvider.getCredential(
idToken: kMockIdToken,
accessToken: kMockAccessToken,
);
final AuthResult result = await auth.signInWithCredential(credential);
verifyAuthResult(result);
final OAuthCredential captured =
verify(mock.signInWithCredential(auth.app.name, captureAny))
.captured
.single;
expect(captured.providerId, equals('apple.com'));
expect(captured.idToken, equals(kMockIdToken));
expect(captured.accessToken, equals(kMockAccessToken));
});

test('PhoneAuthProvider signInWithCredential', () async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: kMockVerificationId,
Expand Down Expand Up @@ -483,6 +500,26 @@ void main() {
expect(captured.accessToken, equals(kMockAccessToken));
});

test('OAuthProvider reauthenticateWithCredential for Apple', () async {
final FirebaseUser user = await auth.currentUser();
OAuthProvider oAuthProvider = OAuthProvider(providerId: "apple.com");
final AuthCredential credential = oAuthProvider.getCredential(
idToken: kMockIdToken,
accessToken: kMockAccessToken,
);
final AuthResult result =
await user.reauthenticateWithCredential(credential);
verifyAuthResult(result);
verify(mock.getCurrentUser(auth.app.name));
final OAuthCredential captured =
verify(mock.reauthenticateWithCredential(auth.app.name, captureAny))
.captured
.single;
expect(captured.providerId, equals('apple.com'));
expect(captured.idToken, equals(kMockIdToken));
expect(captured.accessToken, equals(kMockAccessToken));
});

test('FacebookAuthProvider reauthenticateWithCredential', () async {
final FirebaseUser user = await auth.currentUser();
final AuthCredential credential = FacebookAuthProvider.getCredential(
Expand Down Expand Up @@ -554,6 +591,25 @@ void main() {
expect(captured.accessToken, equals(kMockAccessToken));
});

test('OAuthProvider linkWithCredential for Apple', () async {
OAuthProvider oAuthProvider = OAuthProvider(providerId: "apple.com");
final AuthCredential credential = oAuthProvider.getCredential(
idToken: kMockIdToken,
accessToken: kMockAccessToken,
);
final FirebaseUser user = await auth.currentUser();
final AuthResult result = await user.linkWithCredential(credential);
verifyAuthResult(result);
verify(mock.getCurrentUser(auth.app.name));
final OAuthCredential captured =
verify(mock.linkWithCredential(auth.app.name, captureAny))
.captured
.single;
expect(captured.providerId, equals('apple.com'));
expect(captured.idToken, equals(kMockIdToken));
expect(captured.accessToken, equals(kMockAccessToken));
});

test('FacebookAuthProvider linkWithCredential', () async {
final AuthCredential credential = FacebookAuthProvider.getCredential(
accessToken: kMockAccessToken,
Expand Down Expand Up @@ -757,6 +813,14 @@ void main() {
verify(mock.unlinkFromProvider(auth.app.name, 'google.com'));
});

test('OAuthProvider unlinkFromProvider for Apple', () async {
final FirebaseUser user = await auth.currentUser();
OAuthProvider oAuthProvider = OAuthProvider(providerId: "apple.com");
await user.unlinkFromProvider(oAuthProvider.providerId);
verify(mock.getCurrentUser(auth.app.name));
verify(mock.unlinkFromProvider(auth.app.name, 'apple.com'));
});

test('FacebookAuthProvider unlinkFromProvider', () async {
final FirebaseUser user = await auth.currentUser();
await user.unlinkFromProvider(FacebookAuthProvider.providerId);
Expand Down

0 comments on commit 34ec274

Please sign in to comment.