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
2 changes: 1 addition & 1 deletion packages/firebase_ui_oauth/example/macos/Podfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
platform :osx, '10.12'
platform :osx, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
Expand All @@ -434,7 +434,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
Expand Down Expand Up @@ -492,7 +492,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
Expand Down Expand Up @@ -539,7 +539,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
Expand All @@ -563,7 +563,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
Expand All @@ -586,7 +586,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 12.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
Expand Down
14 changes: 5 additions & 9 deletions packages/firebase_ui_oauth/lib/firebase_ui_oauth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@

export 'package:firebase_auth/firebase_auth.dart' show OAuthCredential;

// Re-export Wasm-compatible libraries instead of `desktop_webview_auth`,
// which imports `dart:io`.
// ignore: implementation_imports
export 'package:desktop_webview_auth/src/auth_result.dart' show AuthResult;
// ignore: implementation_imports
export 'package:desktop_webview_auth/src/provider_args.dart' show ProviderArgs;
export 'package:desktop_webview_auth/google.dart';
export 'package:desktop_webview_auth/facebook.dart';
export 'package:desktop_webview_auth/twitter.dart';
export './src/oauth/auth_result.dart';
export './src/oauth/provider_args.dart';
export './src/oauth/google_sign_in_args.dart';
export './src/oauth/facebook_sign_in_args.dart';
export './src/oauth/twitter_sign_in_args.dart';

export './src/oauth_provider.dart';
export './src/oauth_provider_button_base.dart';
Expand Down
18 changes: 18 additions & 0 deletions packages/firebase_ui_oauth/lib/src/oauth/auth_result.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// The result of a desktop OAuth sign-in flow.
class AuthResult {
final String? accessToken;
final String? idToken;
final String? tokenSecret;

const AuthResult({this.accessToken, this.idToken, this.tokenSecret});

@override
String toString() {
return 'AuthResult(idToken: $idToken, accessToken: $accessToken, '
'tokenSecret: $tokenSecret)';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'auth_result.dart';
import 'oauth_util.dart';
import 'provider_args.dart';

const _responseType = 'token';

class FacebookSignInArgs extends ProviderArgs {
final String clientId;

@override
final String redirectUri;

@override
final host = 'www.facebook.com';

@override
final path = '/v12.0/dialog/oauth';

FacebookSignInArgs({required this.clientId, required this.redirectUri});

String state = '';

@override
Map<String, String> buildQueryParameters() {
state = generateNonce();

return {
'client_id': clientId,
'redirect_uri': redirectUri,
'state': state,
'response_type': _responseType,
};
}
Comment thread
demolaf marked this conversation as resolved.

/// Validates the `state` echoed back by Facebook against the one sent in
/// [buildQueryParameters] before accepting the callback, to guard against
/// CSRF: an attacker tricking the app into completing a sign-in the user
/// never started.
@override
Future<AuthResult?> authorizeFromCallback(String callbackUrl) async {
final uri = Uri.parse(callbackUrl);
final args = usesFragment
? Uri.splitQueryString(uri.fragment)
: uri.queryParameters;

if (args['state'] != state) {
throw Exception('OAuth state mismatch, possible CSRF attempt');
}

return super.authorizeFromCallback(callbackUrl);
}
}
44 changes: 44 additions & 0 deletions packages/firebase_ui_oauth/lib/src/oauth/google_sign_in_args.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'oauth_util.dart';
import 'provider_args.dart';

const _defaultSignInScope = 'https://www.googleapis.com/auth/plus.login';

class GoogleSignInArgs extends ProviderArgs {
final String clientId;
final String scope;
final bool immediate;
final String responseType;

@override
final String redirectUri;

@override
final host = 'accounts.google.com';

@override
final path = '/o/oauth2/v2/auth';

GoogleSignInArgs({
required this.clientId,
required this.redirectUri,
this.scope = _defaultSignInScope,
this.immediate = false,
this.responseType = 'token id_token',
});

@override
Map<String, String> buildQueryParameters() {
return {
'client_id': clientId,
'scope': scope,
'immediate': immediate.toString(),
'response_type': responseType,
'redirect_uri': redirectUri,
'nonce': generateNonce(),
};
}
}
18 changes: 18 additions & 0 deletions packages/firebase_ui_oauth/lib/src/oauth/oauth_util.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:math';

/// Generates a cryptographically secure random nonce, to be included in a
/// credential request.
String generateNonce([int length = 32]) {
const chars =
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
final random = Random.secure();

return List.generate(
length,
(_) => chars[random.nextInt(chars.length)],
).join();
}
48 changes: 48 additions & 0 deletions packages/firebase_ui_oauth/lib/src/oauth/provider_args.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'auth_result.dart';

/// Describes how to build the sign-in URL for a desktop OAuth flow, and how
/// to turn the resulting callback URL into an [AuthResult].
abstract class ProviderArgs {
String get redirectUri;
String get host;
String get path;

Map<String, String> buildQueryParameters();

Future<String> buildSignInUri() async {
final uri = Uri(
scheme: 'https',
host: host,
path: path,
queryParameters: buildQueryParameters(),
);

return uri.toString();
}

bool usesFragment = true;

Future<AuthResult?> authorizeFromCallback(String callbackUrl) async {
final uri = Uri.parse(callbackUrl);
late Map<String, String> args;

if (usesFragment) {
args = Uri.splitQueryString(uri.fragment);
} else {
args = uri.queryParameters;
}

if (args.containsKey('access_token') || args.containsKey('id_token')) {
return AuthResult(
accessToken: args['access_token'],
idToken: args['id_token'],
);
}

throw Exception('No access token found');
}
}
Loading
Loading