Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class Auth0FlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
listOf(
LoginApiRequestHandler(),
LoginWithOtpApiRequestHandler(),
LoginWithFacebookApiRequestHandler(),
MultifactorChallengeApiRequestHandler(),
EmailPasswordlessApiRequestHandler(),
PhoneNumberPasswordlessApiRequestHandler(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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.toMap
import com.auth0.auth0_flutter.request_handlers.MethodCallRequest
import com.auth0.auth0_flutter.utils.assertHasProperties
import io.flutter.plugin.common.MethodChannel

private const val AUTH_LOGIN_WITH_FACEBOOK_METHOD = "auth#loginWithFacebook"

class LoginWithFacebookApiRequestHandler : ApiRequestHandler {
override val method: String = AUTH_LOGIN_WITH_FACEBOOK_METHOD

override fun handle(api: AuthenticationAPIClient, request: MethodCallRequest, result: MethodChannel.Result) {
assertHasProperties(listOf("accessToken"), request.data)

val accessToken = request.data["accessToken"] as String
val profile = request.data["profile"] as? Map<String, Any>
val builder = if (profile != null) {
api.loginWithNativeSocialToken(accessToken, "facebook", profile)
} else {
api.loginWithNativeSocialToken(accessToken, "facebook")
}

// Add optional parameters
(request.data["scopes"] as? List<*>)?.let { scopes ->
builder.setScope((scopes as List<String>).joinToString(" "))
}
(request.data["audience"] as? String)?.let { builder.setAudience(it) }

if (request.data["parameters"] is HashMap<*, *>) {
builder.addParameters(request.data["parameters"] as Map<String, String>)
}

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

override fun onSuccess(credentials: Credentials) {
result.success(credentials.toMap())
}
})
}
}
2 changes: 2 additions & 0 deletions auth0_flutter/darwin/Classes/AuthAPI/AuthAPIHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class AuthAPIHandler: NSObject, FlutterPlugin {
enum Method: String, CaseIterable {
case loginWithUsernameOrEmail = "auth#login"
case loginWithOTP = "auth#loginOtp"
case loginWithFacebook = "auth#loginWithFacebook"
case multifactorChallenge = "auth#multifactorChallenge"
case signup = "auth#signUp"
case userInfo = "auth#userInfo"
Expand Down Expand Up @@ -60,6 +61,7 @@ public class AuthAPIHandler: NSObject, FlutterPlugin {
switch method {
case .loginWithUsernameOrEmail: return AuthAPILoginUsernameOrEmailMethodHandler(client: client)
case .loginWithOTP: return AuthAPILoginWithOTPMethodHandler(client: client)
case .loginWithFacebook: return AuthAPILoginWithFacebookMethodHandler(client: client)
case .multifactorChallenge: return AuthAPIMultifactorChallengeMethodHandler(client: client)
case .signup: return AuthAPISignupMethodHandler(client: client)
case .userInfo: return AuthAPIUserInfoMethodHandler(client: client)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Auth0

#if os(iOS)
import Flutter
#else
import FlutterMacOS
#endif

struct AuthAPILoginWithFacebookMethodHandler: MethodHandler {
enum Argument: String {
case accessToken
case scopes
case parameters
case audience
case profile
}

let client: Authentication

func handle(with arguments: [String: Any], callback: @escaping FlutterResult) {
guard let accessToken = arguments[Argument.accessToken] as? String else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.accessToken.rawValue)))
}
guard let scopes = arguments[Argument.scopes] as? [String] else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.scopes.rawValue)))
}
guard let parameters = arguments[Argument.parameters] as? [String: Any] else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.parameters.rawValue)))
}

let audience = arguments[Argument.audience] as? String
let profile = arguments[Argument.profile] as? [String: Any] ?? [:]

client
.login(facebookSessionAccessToken: accessToken,
profile: profile,
audience: audience,
scope: scopes.asSpaceSeparatedString)
.parameters(parameters)
.start {
switch $0 {
case let .success(credentials): callback(result(from: credentials))
case let .failure(error): callback(FlutterError(from: error))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Auth0

#if os(iOS)
import Flutter
#else
import FlutterMacOS
#endif

struct AuthAPILoginWithFacebookMethodHandler: MethodHandler {
enum Argument: String {
case accessToken
case scopes
case parameters
case audience
case profile
}

let client: Authentication

func handle(with arguments: [String: Any], callback: @escaping FlutterResult) {
guard let accessToken = arguments[Argument.accessToken] as? String else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.accessToken.rawValue)))
}
guard let scopes = arguments[Argument.scopes] as? [String] else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.scopes.rawValue)))
}
guard let parameters = arguments[Argument.parameters] as? [String: Any] else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.parameters.rawValue)))
}

let audience = arguments[Argument.audience] as? String
let profile = arguments[Argument.profile] as? [String: Any] ?? [:]

client
.login(facebookSessionAccessToken: accessToken,
profile: profile,
audience: audience,
scope: scopes.asSpaceSeparatedString)
.parameters(parameters)
.start {
switch $0 {
case let .success(credentials): callback(result(from: credentials))
case let .failure(error): callback(FlutterError(from: error))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Auth0

#if os(iOS)
import Flutter
#else
import FlutterMacOS
#endif

struct AuthAPILoginWithFacebookMethodHandler: MethodHandler {
enum Argument: String {
case accessToken
case scopes
case parameters
case audience
}

let client: Authentication

func handle(with arguments: [String: Any], callback: @escaping FlutterResult) {
guard let accessToken = arguments[Argument.accessToken] as? String else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.accessToken.rawValue)))
}
guard let scopes = arguments[Argument.scopes] as? [String] else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.scopes.rawValue)))
}
guard let parameters = arguments[Argument.parameters] as? [String: Any] else {
return callback(FlutterError(from: .requiredArgumentMissing(Argument.parameters.rawValue)))
}

let audience = arguments[Argument.audience] as? String

client
.login(facebookSessionAccessToken: accessToken,
audience: audience,
scope: scopes.asSpaceSeparatedString)
.parameters(parameters)
.start {
switch $0 {
case let .success(credentials): callback(result(from: credentials))
case let .failure(error): callback(FlutterError(from: error))
}
}
}
}
61 changes: 61 additions & 0 deletions auth0_flutter/lib/src/mobile/authentication_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,67 @@ class AuthenticationApi {
mfaToken: mfaToken,
)));

/// Authenticates the user with a Facebook access token.
/// If successful, it returns a set of tokens, as well as the user's profile
/// (constructed from ID token claims).
///
/// This method requires that you've already obtained a valid Facebook access
/// token from the Facebook SDK. The token will be exchanged for Auth0 tokens.
///
/// ## Endpoint
/// https://auth0.com/docs/api/authentication#social-login-with-provider-s-access-token
///
/// ## Notes
///
/// * [audience] relates to the API Identifier you want to reference in your
/// access tokens. See [API settings](https://auth0.com/docs/get-started/apis/api-settings)
/// to learn more.
/// * [scopes] defaults to `openid profile email offline_access`. You can
/// override these scopes, but `openid` and `profile` are always requested
/// regardless of this setting.
/// * [profile] contains user profile data from Facebook. This should be the
/// profile object obtained from the Facebook SDK.
/// * [parameters] can be used to send through custom parameters to the
/// endpoint to be picked up in a Rule or Action.
///
/// ## Usage example
///
/// ```dart
/// // First, get Facebook access token from Facebook SDK
/// final facebookToken = await getFacebookAccessToken();
/// final facebookProfile = await getFacebookProfile();
///
/// // Then authenticate with Auth0
/// final result = await auth0.api.loginWithFacebook(
/// accessToken: facebookToken,
/// profile: facebookProfile
/// );
/// ```
Future<Credentials> loginWithFacebook({
required final String accessToken,
final String? audience,
final Set<String> scopes = const {
'openid',
'profile',
'email',
'offline_access'
},
final Map<String, String> parameters = const {},
final Map<String, dynamic>? profile,
}) {
// Ensure openid and profile are always included
final effectiveScopes = {...scopes, 'openid', 'profile'};

return Auth0FlutterAuthPlatform.instance
.loginWithFacebook(_createApiRequest(AuthLoginWithSocialTokenOptions(
accessToken: accessToken,
audience: audience,
scopes: effectiveScopes,
parameters: parameters,
profile: profile,
)));
}

/// Requests a challenge for multi-factor authentication (MFA) based on the
/// challenge types supported by the app and user.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export 'src/auth/auth_dpop_headers_options.dart';
export 'src/auth/auth_login_code_options.dart';
export 'src/auth/auth_login_options.dart';
export 'src/auth/auth_login_with_otp_options.dart';
export 'src/auth/auth_login_with_social_token_options.dart';
export 'src/auth/auth_multifactor_challenge_options.dart';
export 'src/auth/auth_passwordless_login_options.dart';
export 'src/auth/auth_passwordless_type.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import '../request/request_options.dart';

class AuthLoginWithSocialTokenOptions implements RequestOptions {
final String accessToken;
final String? audience;
final Set<String> scopes;
final Map<String, String> parameters;
final Map<String, dynamic>? profile;

AuthLoginWithSocialTokenOptions({
required this.accessToken,
this.audience,
this.scopes = const {},
this.parameters = const {},
this.profile,
});

@override
Map<String, dynamic> toMap() => {
'accessToken': accessToken,
'audience': audience,
'scopes': scopes.toList(),
'parameters': parameters,
'profile': profile
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'auth/auth_login_code_options.dart';
import 'auth/auth_login_options.dart';
import 'auth/auth_login_with_otp_options.dart';
import 'auth/auth_login_with_social_token_options.dart';
import 'auth/auth_multifactor_challenge_options.dart';
import 'auth/auth_passwordless_login_options.dart';
import 'auth/auth_renew_access_token_options.dart';
Expand Down Expand Up @@ -37,6 +38,11 @@ abstract class Auth0FlutterAuthPlatform extends PlatformInterface {
throw UnimplementedError('authLoginWithOtp() has not been implemented');
}

Future<Credentials> loginWithFacebook(
final ApiRequest<AuthLoginWithSocialTokenOptions> request) {
throw UnimplementedError('loginWithFacebook() has not been implemented');
}

Future<Challenge> multifactorChallenge(
final ApiRequest<AuthMultifactorChallengeOptions> request) {
throw UnimplementedError('multifactorChallenge() has not been implemented');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'auth/api_exception.dart';
import 'auth/auth_login_code_options.dart';
import 'auth/auth_login_options.dart';
import 'auth/auth_login_with_otp_options.dart';
import 'auth/auth_login_with_social_token_options.dart';
import 'auth/auth_multifactor_challenge_options.dart';
import 'auth/auth_passwordless_login_options.dart';
import 'auth/auth_renew_access_token_options.dart';
Expand All @@ -22,6 +23,7 @@ const MethodChannel _channel = MethodChannel('auth0.com/auth0_flutter/auth');

const String authLoginMethod = 'auth#login';
const String authLoginWithOtpMethod = 'auth#loginOtp';
const String authLoginWithFacebookMethod = 'auth#loginWithFacebook';
const String authMultifactorChallengeMethod = 'auth#multifactorChallenge';
const String authStartPasswordlessWithEmailMethod =
'auth#passwordlessWithEmail';
Expand Down Expand Up @@ -52,6 +54,15 @@ class MethodChannelAuth0FlutterAuth extends Auth0FlutterAuthPlatform {
return Credentials.fromMap(result);
}

@override
Future<Credentials> loginWithFacebook(
final ApiRequest<AuthLoginWithSocialTokenOptions> request) async {
final Map<String, dynamic> result =
await invokeRequest(method: authLoginWithFacebookMethod, request: request);

return Credentials.fromMap(result);
}

@override
Future<Challenge> multifactorChallenge(
final ApiRequest<AuthMultifactorChallengeOptions> request) async {
Expand Down
Loading