diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index c416ed47..e2441b50 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -21,7 +21,6 @@ jobs: outputs: needs_dart_build: ${{ steps.needs_dart_build.outputs.changes }} needs_redis_build: ${{ steps.needs_redis_build.outputs.changes }} - needs_third_party_build: ${{ steps.needs_third_party_build.outputs.changes }} needs_verify: ${{ steps.needs_verify.outputs.changes }} name: 👀 Detect Changes @@ -52,7 +51,6 @@ jobs: - packages/shorebird_cli/** - packages/shorebird_code_push_client/** - packages/shorebird_code_push_protocol/** - - third_party/googleapis_auth/** shorebird_code_push_client: - ./.github/codecov.yml - ./.github/workflows/main.yaml @@ -86,17 +84,6 @@ jobs: - ./.github/actions/dart_package/action.yaml - packages/redis_client/** - - uses: dorny/paths-filter@v3 - name: Redis Detection - id: needs_third_party_build - with: - filters: | - googleapis_auth: - - ./.github/codecov.yml - - ./.github/workflows/main.yaml - - ./.github/actions/dart_package/action.yaml - - third_party/googleapis_auth/** - - uses: dorny/paths-filter@v3 name: Verify Detection id: needs_verify @@ -134,30 +121,6 @@ jobs: with: codecov_token: ${{ secrets.CODECOV_TOKEN }} working_directory: packages/${{ matrix.package }} - min_coverage: 99 - - build_third_party_packages: - needs: changes - if: ${{ needs.changes.outputs.needs_third_party_build != '[]' }} - - strategy: - matrix: - package: ${{ fromJSON(needs.changes.outputs.needs_third_party_build) }} - - runs-on: ubuntu-latest - - name: 🎯 Build ${{ matrix.package }} - - steps: - - name: 📚 Git Checkout - uses: actions/checkout@v4 - - - name: 🎯 Build ${{ matrix.package }} - uses: ./.github/actions/dart_package - with: - codecov_token: ${{ secrets.CODECOV_TOKEN }} - working_directory: third_party/${{ matrix.package }} - min_coverage: 85 build_redis: needs: changes @@ -211,13 +174,7 @@ jobs: ci: needs: - [ - semantic_pull_request, - build_dart_packages, - build_redis, - build_third_party_packages, - verify_packages, - ] + [semantic_pull_request, build_dart_packages, build_redis, verify_packages] if: ${{ always() }} runs-on: ubuntu-latest diff --git a/packages/shorebird_cli/lib/src/auth/auth.dart b/packages/shorebird_cli/lib/src/auth/auth.dart index 85093fc0..af11e542 100644 --- a/packages/shorebird_cli/lib/src/auth/auth.dart +++ b/packages/shorebird_cli/lib/src/auth/auth.dart @@ -37,19 +37,19 @@ const microsoftJwtIssuerPrefix = 'https://login.microsoftonline.com/'; const shorebirdTokenEnvVar = 'SHOREBIRD_TOKEN'; typedef ObtainAccessCredentials = Future Function( - oauth2.AuthEndpoints authEndpoints, oauth2.ClientId clientId, List scopes, http.Client client, - void Function(String) userPrompt, -); + void Function(String) userPrompt, { + oauth2.AuthEndpoints authEndpoints, +}); typedef RefreshCredentials = Future Function( - oauth2.AuthEndpoints authEndpoints, oauth2.ClientId clientId, oauth2.AccessCredentials credentials, - http.Client client, -); + http.Client client, { + oauth2.AuthEndpoints authEndpoints, +}); typedef OnRefreshCredentials = void Function( oauth2.AccessCredentials credentials, @@ -105,7 +105,6 @@ class AuthenticatedClient extends http.BaseClient { if (credentials == null) { final token = _token!; credentials = _credentials = await _refreshCredentials( - token.authProvider.authEndpoints, token.authProvider.clientId, oauth2.AccessCredentials( // This isn't relevant for a refresh operation. @@ -114,6 +113,7 @@ class AuthenticatedClient extends http.BaseClient { token.authProvider.scopes, ), _baseClient, + authEndpoints: token.authProvider.authEndpoints, ); _onRefreshCredentials?.call(credentials); } @@ -123,10 +123,10 @@ class AuthenticatedClient extends http.BaseClient { final authProvider = jwt.authProvider; credentials = _credentials = await _refreshCredentials( - authProvider.authEndpoints, authProvider.clientId, credentials, _baseClient, + authEndpoints: authProvider.authEndpoints, ); _onRefreshCredentials?.call(credentials); } @@ -190,11 +190,11 @@ class Auth { final client = http.Client(); try { final credentials = await _obtainAccessCredentials( - authProvider.authEndpoints, authProvider.clientId, authProvider.scopes, client, prompt, + authEndpoints: authProvider.authEndpoints, ); final codePushClient = _buildCodePushClient( @@ -231,11 +231,11 @@ class Auth { final client = http.Client(); try { _credentials = await _obtainAccessCredentials( - authProvider.authEndpoints, authProvider.clientId, authProvider.scopes, client, prompt, + authEndpoints: authProvider.authEndpoints, ); final codePushClient = _buildCodePushClient(httpClient: this.client); @@ -368,7 +368,7 @@ extension OauthAuthProvider on Jwt { extension OauthValues on AuthProvider { oauth2.AuthEndpoints get authEndpoints => switch (this) { - (AuthProvider.google) => oauth2.GoogleAuthEndpoints(), + (AuthProvider.google) => const oauth2.GoogleAuthEndpoints(), (AuthProvider.microsoft) => MicrosoftAuthEndpoints(), }; diff --git a/packages/shorebird_cli/pubspec.lock b/packages/shorebird_cli/pubspec.lock index a3814bc7..302cb959 100644 --- a/packages/shorebird_cli/pubspec.lock +++ b/packages/shorebird_cli/pubspec.lock @@ -281,13 +281,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "972ff30eebf6a5eab28be3e1e47a45df087ed64d5aefdac0df47758ecdec5385" + url: "https://pub.dev" + source: hosted + version: "0.3.1" googleapis_auth: dependency: "direct main" description: - path: "../../third_party/googleapis_auth" - relative: true - source: path - version: "1.4.1" + name: googleapis_auth + sha256: cafc46446574fd42826aa4cd4d623c94482598fda0a5a5649bf2781bcbc09258 + url: "https://pub.dev" + source: hosted + version: "1.5.0" graphs: dependency: transitive description: @@ -348,10 +357,10 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.7.1" json_annotation: dependency: "direct main" description: @@ -752,10 +761,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + sha256: e7d5ecd604e499358c5fe35ee828c0298a320d54455e791e9dcf73486bc8d9f0 url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "14.1.0" watcher: dependency: transitive description: diff --git a/packages/shorebird_cli/pubspec.yaml b/packages/shorebird_cli/pubspec.yaml index 0563c86f..26927038 100644 --- a/packages/shorebird_cli/pubspec.yaml +++ b/packages/shorebird_cli/pubspec.yaml @@ -17,8 +17,7 @@ dependencies: cli_util: ^0.4.0 collection: ^1.17.1 crypto: ^3.0.2 - googleapis_auth: - path: ../../third_party/googleapis_auth + googleapis_auth: ^1.5.0 http: ^1.0.0 intl: ^0.19.0 io: ^1.0.4 diff --git a/packages/shorebird_cli/test/src/auth/auth_test.dart b/packages/shorebird_cli/test/src/auth/auth_test.dart index bbab282e..c18f5c35 100644 --- a/packages/shorebird_cli/test/src/auth/auth_test.dart +++ b/packages/shorebird_cli/test/src/auth/auth_test.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io' hide Platform; import 'package:cli_util/cli_util.dart'; +import 'package:googleapis_auth/auth_io.dart'; import 'package:googleapis_auth/googleapis_auth.dart' as oauth2; import 'package:http/http.dart' as http; import 'package:jwt/jwt.dart' show Jwt, JwtPayload; @@ -168,8 +169,13 @@ void main() { buildCodePushClient: ({Uri? hostedUri, http.Client? httpClient}) { return codePushClient; }, - obtainAccessCredentials: - (authEndpoints, clientId, scopes, client, userPrompt) async { + obtainAccessCredentials: ( + clientId, + scopes, + client, + userPrompt, { + AuthEndpoints authEndpoints = const GoogleAuthEndpoints(), + }) async { return accessCredentials; }, ), @@ -208,9 +214,13 @@ void main() { () => AuthenticatedClient.token( token: ciToken, httpClient: httpClient, - refreshCredentials: - (authEndpoints, clientId, credentials, client) async => - accessCredentials, + refreshCredentials: ( + clientId, + credentials, + client, { + AuthEndpoints authEndpoints = const GoogleAuthEndpoints(), + }) async => + accessCredentials, ), returnsNormally, ); @@ -231,9 +241,13 @@ void main() { token: ciToken, httpClient: httpClient, onRefreshCredentials: onRefreshCredentialsCalls.add, - refreshCredentials: - (authEndpoints, clientId, credentials, client) async => - accessCredentials, + refreshCredentials: ( + clientId, + credentials, + client, { + AuthEndpoints authEndpoints = const GoogleAuthEndpoints(), + }) async => + accessCredentials, ); await runWithOverrides( @@ -265,9 +279,13 @@ void main() { token: ciToken, httpClient: httpClient, onRefreshCredentials: onRefreshCredentialsCalls.add, - refreshCredentials: - (authEndpoints, clientId, credentials, client) async => - accessCredentials, + refreshCredentials: ( + clientId, + credentials, + client, { + AuthEndpoints authEndpoints = const GoogleAuthEndpoints(), + }) async => + accessCredentials, ); await runWithOverrides( @@ -315,9 +333,13 @@ void main() { credentials: expiredCredentials, httpClient: httpClient, onRefreshCredentials: onRefreshCredentialsCalls.add, - refreshCredentials: - (authEndpoints, clientId, credentials, client) async => - accessCredentials, + refreshCredentials: ( + clientId, + credentials, + client, { + AuthEndpoints authEndpoints = const GoogleAuthEndpoints(), + }) async => + accessCredentials, ); await runWithOverrides( diff --git a/third_party/googleapis_auth/.gitignore b/third_party/googleapis_auth/.gitignore deleted file mode 100644 index 547e3a0d..00000000 --- a/third_party/googleapis_auth/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.dart_tool/ -.packages -pubspec.lock - -# User files -test_integration/*.json -test_integration/client_id*.yaml -test_integration/config.yaml diff --git a/third_party/googleapis_auth/CHANGELOG.md b/third_party/googleapis_auth/CHANGELOG.md deleted file mode 100644 index 54bca859..00000000 --- a/third_party/googleapis_auth/CHANGELOG.md +++ /dev/null @@ -1,193 +0,0 @@ -## 1.4.1 - -- Require Dart 2.19 or later. -- Allow latest `package:http`. - -## 1.4.0 - -- Update `README` to include a warning about Flutter application usage. -- Require Dart 2.17 or later. - -#### `googlapis_auth.dart` - -- `authenticatedClient` function: added optional `bool closeUnderlyingClient` - parameter. - -#### `auth_browser.dart` library - -- Added `AuthenticationException` and use it instead of `Exception` or - `StateError` in many cases where authentication can fail. -- Added `requestAccessCredentials`, `requestAuthorizationCode`, `revokeConsent`, - and `CodeResponse` to support the new - [Google Identity Services](https://developers.google.com/identity/oauth2/web/guides/overview). -- Deprecated `createImplicitBrowserFlow` function. - -#### `auth_io.dart` library - -- Added an optional `listenPort` parameter to `clientViaUserConsent` - and `obtainAccessCredentialsViaUserConsent`. - -## 1.3.1 - -- Include `plugin_name` during browser authorization. - -## 1.3.0 - -- The `secret` param in `ClientId` constructor is now optional. -- Use the latest supported Google OAuth 2.0 URL -- `auth_browser` library: - - Migrated to newer `auth2` Javascript API. - - Added support for `hostedDomain` to all applicable functions. - - `createImplicitBrowserFlow`: added (unsupported) `enableDebugLogs` param. - (Maybe helpful for debugging, but should not be used in production.) -- `auth_io` library: - - Generate a longer, secure random state token. - - Implement code verifier logic for the desktop auth flows. See - https://developers.google.com/identity/protocols/oauth2/native-app#create-code-challenge - - `obtainAccessCredentialsViaCodeExchange` - - `scopes` are now acquired from the initial API call and not via a separate - API call to the `tokeninfo` endpoint. - - Added optional `codeVerifier` parameter. - -## 1.2.0 - -- Added an optional `hostedDomain` parameter to many functions in - `auth_io.dart`. If provided, restricts sign-in to Google Apps hosted accounts - at that domain. -- Fix an error when doing OAUTH code exchanged with an undefined secret. -- `clientViaApiKey` is now exported from `googleapis_auth.dart`. -- Added `String? details` to `UserConsentException`. -- Update the host used to access metadata on Google Cloud. From - `http://metadata/` to `http://metadata.google.internal`. -- Require Dart 2.13 -- Deprecated `RefreshFailedException` - `ServerRequestFailedException` is used - instead. - -## 1.1.0 - -- Added the `googleapis_auth.dart` library. It is convention to have the default - library within a package align with the package name. `auth.dart` is now - deprecated and will be removed in v2. -- Added `fromJson` factory and `toJson` method to `AccessToken`, - `AccessCredentials`, and `ClientId`. -- Remove dynamic function invocations. - -## 1.0.0 - -- Add support for null-safety. -- Require Dart 2.12 or later. - -## 0.2.12+1 - -- Removed a `dart:async` import that isn't required for \>=Dart 2.1. -- Require \>=Dart 2.1. - -## 0.2.12 - -- Add `clientViaApplicationDefaultCredentials` for obtaining credentials using - [ADC](https://cloud.google.com/docs/authentication/production). - -## 0.2.11+1 - -- Fix 'multiple completer completion' bug in `ImplicitFlow`. - -## 0.2.11 - -- Add the `force` parameter to the `obtainAccessCredentialsViaUserConsent` API. - -## 0.2.10 - -- Look for GCE metadata host in environment under `$GCE_METADATA_HOST`. - -## 0.2.9 - -- Prepare for [Uint8List SDK breaking change](Prepare for Uint8List SDK breaking - change). - -## 0.2.8 - -- Initialize implicit browser flows statically, allowing multiple ImplicitFlow - objects to initialize without trying to load the gapi JavaScript library - multiple times. - -## 0.2.7 - -- Support for specifying desired `ResponseType`, allowing applications to obtain - an `id_token` using `ImplicitBrowserFlow`. - -## 0.2.6 - -- Ignore script loading error after timeout for in-browser implicit login-flow. - -## 0.2.5+3 - -- Support `package:http` `>=0.11.3+17 <0.13.0`. - -## 0.2.5+2 - -- Support Dart 2. - -## 0.2.5+1 - -- Switch all uppercase constants from `dart:convert` to lowercase. - -## 0.2.5 - -- Add an optional `loginHint` parameter to browser oauth2 flow APIs which can be - used to specify a hint as to which user is being logged in. - -## 0.2.4 - -- Added `id_token` to `AccessCredentials` - -- Migrated to Dart 2 `BigInt`. - -## 0.2.3+6 - -- Fix async issue in oauth2 flow implementation - -## 0.2.3+5 - -- Support the latest version of `crypto` package. - -## 0.2.3+4 - -- Make package strong-mode compliant. - -## 0.2.3+3 - -- Support package:crypto >= 0.9.2 - -## 0.2.3+2 - -- Use preferred "Metadata-Flavor" HTTP header in - `MetadataServerAuthorizationFlow` instead of the deprecated - "X-Google-Metadata-Request" header. - -## 0.2.3 - -- Allow `ServiceAccountCredentials` constructors to take an optional `user` - argument to specify a user to impersonate. - -## 0.2.2 - -- Allow `ServiceAccountCredentials.fromJson` to accept a `Map`. -- Cleaned up `README.md` - -## 0.2.1 - -- Added optional `force` and `immediate` arguments to `runHybridFlow`. - -## 0.2.0 - -- Renamed `forceUserConsent` parameter to `immediate`. -- Added `runHybridFlow` function to `auth_browser`, with corresponding - `HybridFlowResult` class. - -## 0.1.1 - -- Add `clientViaApiKey` functions to `auth_io` ad `auth_browser`. - -## 0.1.0 - -- First release. diff --git a/third_party/googleapis_auth/LICENSE b/third_party/googleapis_auth/LICENSE deleted file mode 100644 index 000cd7be..00000000 --- a/third_party/googleapis_auth/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2014, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/googleapis_auth/README.md b/third_party/googleapis_auth/README.md deleted file mode 100644 index 6af278e6..00000000 --- a/third_party/googleapis_auth/README.md +++ /dev/null @@ -1,331 +0,0 @@ -Provides support for obtaining OAuth2 credentials to access Google APIs. - -This package also provides convenience functionality for: - -- obtaining authenticated HTTP clients -- automatically refreshing OAuth2 credentials - -> Do _**NOT**_ use this package (`package:googleapis_auth`) with a -> [Flutter](https://flutter.dev/) application. -> -> Use -> [package:extension_google_sign_in_as_googleapis_auth](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth) -> instead. - -### Using this package - -Using this package requires creating a Google Cloud Project and obtaining -application credentials for the specific application type. The steps required -are: - -- Create a new Google Cloud Project on the - [Google Developers Console](https://console.developers.google.com) -- Enable all APIs that the application will use on the - [Google Developers Console](https://console.developers.google.com) (under - DevConsole -> Project -> APIs & auth -> APIs) -- Obtain application credentials for a specific application type on the - [Google Developers Console](https://console.developers.google.com) (under - DevConsole -> Project -> APIs & auth -> Credentials) -- Use the `googleapis_auth` package to obtain access credentials / obtain an - authenticated HTTP client. - -Depending on the application type, there are different ways to achieve the third -and fourth step. The following is a list of supported OAuth2 flows with a -description of these two steps. - -#### Client-side Web Application - -For client-side only web applications a "Client ID" needs to be created (under -DevConsole -> Project -> APIs & auth -> Credentials). When creating a new client -ID, select the "Web application" type. For client-side only applications, no -`Redirect URIs` are necessary. The `Javascript Origins` setting must be set to -all URLs on which your application will be served (e.g. http://localhost:8080 -for local testing). - -After the Client ID has been created, you can obtain access credentials via - -```dart -import 'package:googleapis_auth/auth_browser.dart'; - -// Initialize the browser oauth2 flow functionality then use it to obtain credentials. -Future obtainCredentials() async { - final flow = await createImplicitBrowserFlow( - ClientId('....apps.googleusercontent.com'), - ['scope1', 'scope2'], - ); - - try { - return await flow.obtainAccessCredentialsViaUserConsent(); - } finally { - flow.close(); - } -} -``` - -or obtain an authenticated HTTP client via - -```dart -import 'package:googleapis_auth/auth_browser.dart'; - -// Initialize the browser oauth2 flow functionality then use it to -// get an authenticated and auto refreshing client. -Future obtainAuthenticatedClient() async { - final flow = await createImplicitBrowserFlow( - ClientId('....apps.googleusercontent.com'), - ['scope1', 'scope2'], - ); - - try { - return await flow.clientViaUserConsent(); - } finally { - flow.close(); - } -} -``` - -To prevent popup blockers from blocking the user authorization dialog, the -methods `obtainAccessCredentialsViaUserConsent` and `clientViaUserConsent` -should preferably only be called inside an event handler, since most browsers do -not block popup windows created in response to a user interaction. - -The authenticated HTTP client can now access data on behalf a user for the -requested oauth2 scopes. - -#### Installed/Console Application - -For installed/console applications a "Client ID" needs to be created (under -DevConsole -> Project -> APIs & auth -> Credentials). When creating a new client -ID, select the "Installed application -> Other" type. - -The redirect URIs for the automatic and manual flow will be configured -automatically. - -After the Client ID has been created, you can obtain access credentials via - -```dart -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart' as http; - -// Use the oauth2 authentication code flow functionality to obtain -// credentials. [prompt] is used for directing the user to a URI. -Future obtainCredentials() async { - final client = http.Client(); - - try { - return await obtainAccessCredentialsViaUserConsent( - ClientId('....apps.googleusercontent.com', '...'), - ['scope1', 'scope2'], - client, - _prompt, - ); - } finally { - client.close(); - } -} - -void _prompt(String url) { - print('Please go to the following URL and grant access:'); - print(' => $url'); - print(''); -} -``` - -or obtain an authenticated HTTP client via - -```dart -import 'package:googleapis_auth/auth_io.dart'; - -// Use the oauth2 code grant server flow functionality to -// get an authenticated and auto refreshing client. -Future obtainCredentials() async => await clientViaUserConsent( - ClientId('....apps.googleusercontent.com', '...'), - ['scope1', 'scope2'], - _prompt, -); - -void _prompt(String url) { - print('Please go to the following URL and grant access:'); - print(' => $url'); - print(''); -} -``` - -The Client ID must be created with a `client_secret` here, however there is no -way to properly secure a `client_secret` for installed/console applications. -Fortunately the OAuth2 flow used in this case -[assumes that the app cannot keep secrets](https://developers.google.com/identity/protocols/oauth2/native-app) -so this particular `client_secret` does not need to be kept secret. You should -however make sure not to re-use the same `client_secret` anywhere secrecy is -required. - -In case of misconfigured browsers/proxies or other issues, it is also possible -to use a manual flow via `obtainAccessCredentialsViaUserConsentManual` and -`clientViaUserConsentManual`. But in this case the `prompt` function needs to -complete with a `Future` which contains the "authorization code". The -user obtains the "authorization code" (which is a string of characters) in a -browser and needs to copy & paste it to the application. (The prompt function -should block until it has gotten the "authorization code" from the user.) - -The authenticated HTTP client can now access data on behalf a user for the -requested oauth2 scopes. - -#### Autonomous Application / Service Account - -If an application wants to act autonomously and access e.g. data from a Google -Cloud Project, then a Service Account can be created. In this case no user -authorization is involved. - -A service account can be created via the "Service account" application type when -creating a Client ID (under DevConsole -> Project -> APIs & auth -> -Credentials). It will download a JSON document which contains a private RSA key. -That private key is used for obtaining access credentials. - -After the service account was created, you can obtain access credentials via - -```dart -import "package:googleapis_auth/auth_io.dart"; -import "package:http/http.dart" as http; - -... - -// Use service account credentials to obtain oauth credentials. -Future obtainCredentials() async { - var accountCredentials = ServiceAccountCredentials.fromJson({ - "private_key_id": "", - "private_key": "", - "client_email": "@developer.gserviceaccount.com", - "client_id": ".apps.googleusercontent.com", - "type": "service_account" - }); - var scopes = [...]; - - var client = http.Client(); - AccessCredentials credentials = - await obtainAccessCredentialsViaServiceAccount(accountCredentials, scopes, client); - - client.close(); - return credentials; -} -``` - -or an authenticated HTTP client via - -```dart -import "package:googleapis_auth/auth_io.dart"; - -... - -// Use service account credentials to get an authenticated and auto refreshing client. -Future obtainAuthenticatedClient() async { - final accountCredentials = ServiceAccountCredentials.fromJson({ - "private_key_id": "", - "private_key": "", - "client_email": "@developer.gserviceaccount.com", - "client_id": ".apps.googleusercontent.com", - "type": "service_account" - }); - var scopes = [...]; - - AuthClient client = await clientViaServiceAccount(accountCredentials, scopes); - - return client; // Remember to close the client when you are finished with it. -} -``` - -The authenticated HTTP client can now access APIs. - -##### Impersonation - -For some APIs the use of a service account also requires to impersonate a user. -To support that the `ServiceAccountCredentials` constructors have an optional -argument `impersonatedUser` to specify the user to impersonate. - -One example of this are the Google Apps APIs. See -[Perform Google Apps Domain-Wide Delegation of Authority](https://developers.google.com/admin-sdk/directory/v1/guides/delegation) -for information on the additional security configuration required to enable this -for a service account. - -#### Autonomous Application / Compute Engine using metadata service - -If an application wants to act autonomously and access e.g. data from a Google -Cloud Project, then a Service Account can be used. In case the application is -running on a ComputeEngine VM it is possible to start a VM with a set of scopes -the VM is allowed to use. See the -[documentation](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#using) -for further information. - -Here is an example of using the metadata service for obtaining access -credentials on a ComputeEngine VM. - -```dart -import "package:googleapis_auth/auth_io.dart"; -import "package:http/http.dart" as http; - -... - -// Use the metadata service to obtain oauth credentials. -Future obtainCredentials() async { - var client = http.Client(); - - AccessCredentials credentials = - await obtainAccessCredentialsViaMetadataServer(client); - - client.close(); - return credentials; -} -``` - -or an authenticated HTTP client via - -```dart -import "package:googleapis_auth/auth_io.dart"; - -... - -// Use the metadata service to get an authenticated and auto refreshing client. -Future obtainAuthenticatedClient() async { - - AuthClient client = await clientViaMetadataServer(); - - return client; // Remember to close the client when you are finished with it. -} -``` - -The authenticated HTTP client can now access APIs. - -#### Accessing Public Data with API Key - -It is possible to access some APIs by just using an API key without OAuth2. - -An API key can be obtained on the Google Developers Console by creating a Key at -the "Public API access" section (under DevConsole -> Project -> APIs & auth -> -Credentials). - -A key can be created for different application types: For browser applications -it is necessary to specify a set of referer URls from which the application -would like to access APIs. For server applications it is possible to specify a -list of IP ranges from which the client application would like to access APIs. - -Note that the ApiKey is used for quota and billing purposes and should not be -disclosed to third parties. - -Here is an example of getting an HTTP client which uses an API key for making -HTTP requests. - -```dart -import "package:googleapis_auth/auth_io.dart"; - -var client = clientViaApiKey(''); -// [client] can now be used to make REST calls to Google APIs. - -... - -client.close(); -``` - -### More information - -More information can be obtained from official Google Developers documentation: - -- [OAuth2 to Access Google APIs](https://developers.google.com/identity/protocols/oauth2) -- [OAuth2 Playground](https://developers.google.com/oauthplayground/) diff --git a/third_party/googleapis_auth/lib/auth.dart b/third_party/googleapis_auth/lib/auth.dart deleted file mode 100644 index d436be28..00000000 --- a/third_party/googleapis_auth/lib/auth.dart +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2021, the Dart 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. - -// ignore_for_file: comment_references - -/// This library has been deprecated. Use [googleapis_auth] instead. -@Deprecated('Use `googleapis_auth.dart` instead') -library auth; - -export 'googleapis_auth.dart'; diff --git a/third_party/googleapis_auth/lib/auth_browser.dart b/third_party/googleapis_auth/lib/auth_browser.dart deleted file mode 100644 index d5fbf271..00000000 --- a/third_party/googleapis_auth/lib/auth_browser.dart +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; - -import 'package:http/browser_client.dart'; -import 'package:http/http.dart'; - -import 'src/auth_functions.dart'; -import 'src/auth_http_utils.dart'; -import 'src/http_client_base.dart'; -import 'src/oauth2_flows/implicit.dart'; -import 'src/oauth2_flows/token_model.dart'; -import 'src/service_account_credentials.dart'; - -export 'googleapis_auth.dart'; -export 'src/authentication_exception.dart' show AuthenticationException; -export 'src/oauth2_flows/token_model.dart' - show - CodeResponse, - requestAccessCredentials, - requestAuthorizationCode, - revokeConsent; - -/// Will create and complete with a [BrowserOAuth2Flow] object. -/// -/// {@template googleapis_auth_gis_deprecated} -/// This member is *deprecated*. Use [requestAccessCredentials] or -/// [requestAuthorizationCode] instead. -/// {@endtemplate} -/// -/// This function will perform an implicit browser based oauth2 flow. -/// -/// It will load Google's `gapi` library and initialize it. After initialization -/// it will complete with a [BrowserOAuth2Flow] object. The flow object can be -/// used to obtain `AccessCredentials` or an authenticated HTTP client. -/// -/// If loading or initializing the `gapi` library results in an error, this -/// future will complete with an error. -/// -/// {@macro googleapis_auth_clientId_param} -/// -/// {@template googleapis_auth_baseClient_param} -/// If [baseClient] is provided, all HTTP requests will be made with it. -/// Otherwise, a new [Client] instance will be created. -/// {@endtemplate} -/// -/// {@macro googleapis_auth_close_the_client} -/// {@macro googleapis_auth_not_close_the_baseClient} -@Deprecated( - 'This member is deprecated. Use requestAccessCredentials or ' - 'requestAuthorizationCode instead.', -) -Future createImplicitBrowserFlow( - ClientId clientId, - List scopes, { - Client? baseClient, - @Deprecated( - 'Undocumented feature. May help debugging. ' - 'Do not include in production code.', - ) - bool enableDebugLogs = false, -}) async { - final refCountedClient = baseClient == null - ? RefCountedClient(BrowserClient()) - : RefCountedClient(baseClient, initialRefCount: 2); - - final flow = ImplicitFlow(clientId.identifier, scopes, enableDebugLogs); - - try { - await flow.initialize(); - } catch (_) { - refCountedClient.close(); - rethrow; - } - return BrowserOAuth2Flow._(flow, refCountedClient); -} - -/// Used for obtaining oauth2 access credentials. -/// -/// {@macro googleapis_auth_gis_deprecated} -/// -/// Warning: -/// -/// The methods [obtainAccessCredentialsViaUserConsent] and -/// [clientViaUserConsent] try to open a popup window for the user authorization -/// dialog. -/// -/// In order to prevent browsers from blocking the popup window, these -/// methods should only be called inside an event handler, since most -/// browsers do not block popup windows created in response to a user -/// interaction. -@Deprecated( - 'This member is deprecated. Use requestAccessCredentials or ' - 'requestAuthorizationCode instead.', -) -class BrowserOAuth2Flow { - final ImplicitFlow _flow; - final RefCountedClient _client; - - bool _wasClosed = false; - - /// The HTTP client passed in will be closed if `close` was called and all - /// generated HTTP clients via [clientViaUserConsent] were closed. - BrowserOAuth2Flow._(this._flow, this._client); - - /// Obtain oauth2 [AccessCredentials]. - /// - /// {@template googleapis_auth_force} - /// If [force] is `true` this will create a popup window and ask the user to - /// grant the application offline access. In case the user is not already - /// logged in, they will be presented with an login dialog first. - /// - /// If [force] is `false` this will only create a popup window if the user - /// has not already granted the application access. - /// {@endtemplate} - /// - /// {@template googleapis_auth_immediate} - /// If [immediate] is `true` there will be no user involvement. If the user - /// is either not logged in or has not already granted the application access, - /// a `UserConsentException` will be thrown. - /// - /// If [immediate] is `false` the user might be asked to login (if not - /// already logged in) and might get asked to grant the application access - /// (if the application hasn't been granted access before). - /// {@endtemplate} - /// - /// {@template googleapis_auth_loginHint} - /// If [loginHint] is not `null`, it will be passed to the server as a hint - /// to which user is being signed-in. This can e.g. be an email or a User ID - /// which might be used as pre-selection in the sign-in flow. - /// {@endtemplate} - /// - /// {@macro googleapis_auth_hostedDomain_param} - /// - /// If [responseTypes] is not `null` or empty, it will be sent to the server - /// to inform the server of the type of responses to reply with. - /// - /// {@template googleapis_auth_user_consent_return} - /// The returned [Future] will complete with [AccessCredentials] if the user - /// has given the application access to their data. - /// Otherwise, a [UserConsentException] will be thrown. - /// {@endtemplate} - /// - /// {@macro googleapis_auth_hostedDomain_param} - Future obtainAccessCredentialsViaUserConsent({ - bool force = false, - bool immediate = false, - String? loginHint, - List? responseTypes, - String? hostedDomain, - }) { - _ensureOpen(); - return _flow.login( - prompt: _promptFromBooleans(force, immediate), - loginHint: loginHint, - responseTypes: responseTypes, - hostedDomain: hostedDomain, - ); - } - - /// Obtains [AccessCredentials] and returns an authenticated HTTP client. - /// - /// {@template googleapis_auth_returned_auto_refresh_client} - /// HTTP requests made on the returned client will get an additional - /// `Authorization` header with the [AccessCredentials] obtained. - /// Once the [AccessCredentials] expire, it will use it's refresh token - /// (if available) to obtain new credentials. - /// See [autoRefreshingClient] for more information. - /// {@endtemplate} - /// - /// See [obtainAccessCredentialsViaUserConsent] for how credentials will be - /// obtained. Errors from [obtainAccessCredentialsViaUserConsent] will be let - /// through to the returned `Future` of this function and to the returned - /// HTTP client (in case of credential refreshes). - /// - /// The returned HTTP client will forward errors from lower levels via it's - /// `Future` or it's `Response.read()` stream. - /// - /// {@macro googleapis_auth_immediate} - /// - /// {@macro googleapis_auth_close_the_client} - /// - /// {@macro googleapis_auth_loginHint} - /// - /// {@macro googleapis_auth_hostedDomain_param} - Future clientViaUserConsent({ - bool immediate = false, - String? loginHint, - String? hostedDomain, - }) async { - final credentials = await obtainAccessCredentialsViaUserConsent( - immediate: immediate, - loginHint: loginHint, - hostedDomain: hostedDomain, - ); - return _clientFromCredentials(credentials); - } - - /// Obtains [AccessCredentials] and an authorization code which can be - /// exchanged for permanent access credentials. - /// - /// Use case: - /// A web application might want to get consent for accessing data on behalf - /// of a user. The client part is a dynamic webapp which wants to open a - /// popup which asks the user for consent. The webapp might want to use the - /// credentials to make API calls, but the server may want to have offline - /// access to user data as well. - /// - /// {@macro googleapis_auth_force} - /// - /// {@macro googleapis_auth_immediate} - /// - /// {@macro googleapis_auth_loginHint} - /// - /// {@macro googleapis_auth_hostedDomain_param} - Future runHybridFlow({ - bool force = true, - bool immediate = false, - String? loginHint, - String? hostedDomain, - }) async { - _ensureOpen(); - final result = await _flow.loginHybrid( - prompt: _promptFromBooleans(force, immediate), - hostedDomain: hostedDomain, - loginHint: loginHint, - ); - return HybridFlowResult(this, result.credential, result.code); - } - - /// Will close this [BrowserOAuth2Flow] object and the HTTP [Client] it is - /// using. - /// - /// The clients obtained via [clientViaUserConsent] will continue to work. - /// The client obtained via `newClient` of obtained [HybridFlowResult] objects - /// will continue to work. - /// - /// After this flow object and all obtained clients were closed the underlying - /// HTTP client will be closed as well. - /// - /// After calling this `close` method, calls to [clientViaUserConsent], - /// [obtainAccessCredentialsViaUserConsent] and to `newClient` on returned - /// [HybridFlowResult] objects will fail. - void close() { - _ensureOpen(); - _wasClosed = true; - _client.close(); - } - - void _ensureOpen() { - if (_wasClosed) { - throw StateError('BrowserOAuth2Flow has already been closed.'); - } - } - - AutoRefreshingAuthClient _clientFromCredentials(AccessCredentials cred) { - _ensureOpen(); - _client.acquire(); - return _AutoRefreshingBrowserClient(_client, cred, _flow); - } -} - -/// Represents the result of running a browser based hybrid flow. -/// -/// {@macro googleapis_auth_gis_deprecated} -/// -/// The `credentials` field holds credentials which can be used on the client -/// side. The `newClient` function can be used to make a new authenticated HTTP -/// client using these credentials. -/// -/// The `authorizationCode` can be sent to the server, which knows the -/// "client secret" and can exchange it with long-lived access credentials. -/// -/// See the `obtainAccessCredentialsViaCodeExchange` function in the -/// `googleapis_auth.auth_io` library for more details on how to use the -/// authorization code. -@Deprecated( - 'This member is deprecated. Use requestAccessCredentials or ' - 'requestAuthorizationCode instead.', -) -class HybridFlowResult { - final BrowserOAuth2Flow _flow; - - /// Access credentials for making authenticated HTTP requests. - final AccessCredentials credentials; - - /// The authorization code received from the authorization endpoint. - /// - /// The auth code can be used to receive permanent access credentials. - /// This requires a confidential client which can keep a secret. - final String? authorizationCode; - - HybridFlowResult(this._flow, this.credentials, this.authorizationCode); - - AutoRefreshingAuthClient newClient() { - _flow._ensureOpen(); - return _flow._clientFromCredentials(credentials); - } -} - -class _AutoRefreshingBrowserClient extends AutoRefreshDelegatingClient { - @override - AccessCredentials credentials; - final ImplicitFlow _flow; - Client _authClient; - - _AutoRefreshingBrowserClient(super.client, this.credentials, this._flow) - : _authClient = authenticatedClient(client, credentials); - - @override - Future send(BaseRequest request) async { - if (!credentials.accessToken.hasExpired) { - return _authClient.send(request); - } - credentials = await _flow.login(prompt: 'none'); - notifyAboutNewCredentials(credentials); - _authClient = authenticatedClient(baseClient, credentials); - return _authClient.send(request); - } -} - -String? _promptFromBooleans(bool force, bool immediate) { - if (force) { - if (immediate) { - throw ArgumentError.value( - immediate, - 'immediate', - 'Cannot be true if `force` is also true.', - ); - } - return 'consent'; - } - if (immediate) { - return 'none'; - } - return null; -} diff --git a/third_party/googleapis_auth/lib/auth_io.dart b/third_party/googleapis_auth/lib/auth_io.dart deleted file mode 100644 index c146988e..00000000 --- a/third_party/googleapis_auth/lib/auth_io.dart +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (c) 2014, the Dart 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 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart'; - -import 'src/auth_http_utils.dart'; -import 'src/oauth2_flows/authorization_code_grant_manual_flow.dart'; -import 'src/oauth2_flows/authorization_code_grant_server_flow.dart'; - -export 'googleapis_auth.dart'; -export 'src/metadata_server_client.dart'; -export 'src/oauth2_flows/auth_code.dart' - show obtainAccessCredentialsViaCodeExchange; -export 'src/service_account_client.dart'; -export 'src/typedefs.dart'; - -/// Obtains oauth2 credentials and returns an authenticated HTTP client. -/// -/// See [obtainAccessCredentialsViaUserConsent] for specifics about the -/// arguments used for obtaining access credentials. -/// -/// {@macro googleapis_auth_clientId_param} -/// -/// {@macro googleapis_auth_returned_auto_refresh_client} -/// -/// {@macro googleapis_auth_baseClient_param} -/// -/// {@template googleapis_auth_hostedDomain_param} -/// If provided, restricts sign-in to Google Apps hosted accounts at -/// [hostedDomain]. For more details, see -/// https://developers.google.com/identity/protocols/oauth2/openid-connect#hd-param -/// {@endtemplate} -/// -/// {@macro googleapis_auth_close_the_client} -/// {@macro googleapis_auth_not_close_the_baseClient} -/// {@macro googleapis_auth_listen_port} -Future clientViaUserConsent( - AuthEndpoints authEndpoints, - ClientId clientId, - List scopes, - PromptUserForConsent userPrompt, { - Client? baseClient, - String? hostedDomain, - int listenPort = 0, -}) async { - var closeUnderlyingClient = false; - if (baseClient == null) { - baseClient = Client(); - closeUnderlyingClient = true; - } - - final flow = AuthorizationCodeGrantServerFlow( - authEndpoints, - clientId, - scopes, - baseClient, - userPrompt, - hostedDomain: hostedDomain, - listenPort: listenPort, - ); - - AccessCredentials credentials; - - try { - credentials = await flow.run(); - } catch (e) { - if (closeUnderlyingClient) { - baseClient.close(); - } - rethrow; - } - return AutoRefreshingClient( - baseClient, - authEndpoints, - clientId, - credentials, - closeUnderlyingClient: closeUnderlyingClient, - ); -} - -/// Obtains oauth2 credentials and returns an authenticated HTTP client. -/// -/// See [obtainAccessCredentialsViaUserConsentManual] for specifics about the -/// arguments used for obtaining access credentials. -/// -/// {@macro googleapis_auth_clientId_param} -/// -/// {@macro googleapis_auth_returned_auto_refresh_client} -/// -/// {@macro googleapis_auth_baseClient_param} -/// -/// {@macro googleapis_auth_hostedDomain_param} -/// -/// {@macro googleapis_auth_close_the_client} -/// {@macro googleapis_auth_not_close_the_baseClient} -Future clientViaUserConsentManual( - AuthEndpoints authEndpoints, - ClientId clientId, - List scopes, - PromptUserForConsentManual userPrompt, { - Client? baseClient, - String? hostedDomain, -}) async { - var closeUnderlyingClient = false; - if (baseClient == null) { - baseClient = Client(); - closeUnderlyingClient = true; - } - - final flow = AuthorizationCodeGrantManualFlow( - authEndpoints, - clientId, - scopes, - baseClient, - userPrompt, - hostedDomain: hostedDomain, - ); - - AccessCredentials credentials; - - try { - credentials = await flow.run(); - } catch (e) { - if (closeUnderlyingClient) { - baseClient.close(); - } - rethrow; - } - - return AutoRefreshingClient( - baseClient, - authEndpoints, - clientId, - credentials, - closeUnderlyingClient: closeUnderlyingClient, - ); -} - -/// Obtain oauth2 [AccessCredentials] using the oauth2 authentication code flow. -/// -/// {@macro googleapis_auth_clientId_param} -/// -/// [userPrompt] will be used for directing the user/user-agent to a URI. See -/// [PromptUserForConsent] for more information. -/// -/// {@macro googleapis_auth_client_for_creds} -/// -/// {@macro googleapis_auth_hostedDomain_param} -/// -/// {@macro googleapis_auth_user_consent_return} -/// -/// {@template googleapis_auth_listen_port} -/// The `localhost` port to use when listening for the redirect from a user -/// browser interaction. Defaults to `0` - which means the port is dynamic. -/// -/// Generally you want to specify an explicit port so you can configure it -/// on the Google Cloud console. -/// {@endtemplate} -Future obtainAccessCredentialsViaUserConsent( - AuthEndpoints authEndpoints, - ClientId clientId, - List scopes, - Client client, - PromptUserForConsent userPrompt, { - String? hostedDomain, - int listenPort = 0, -}) => - AuthorizationCodeGrantServerFlow( - authEndpoints, - clientId, - scopes, - client, - userPrompt, - hostedDomain: hostedDomain, - listenPort: listenPort, - ).run(); - -/// Obtain oauth2 [AccessCredentials] using the oauth2 authentication code flow. -/// -/// {@macro googleapis_auth_clientId_param} -/// -/// [userPrompt] will be used for directing the user/user-agent to a URI. See -/// [PromptUserForConsentManual] for more information. -/// -/// {@macro googleapis_auth_client_for_creds} -/// -/// {@macro googleapis_auth_hostedDomain_param} -/// -/// {@macro googleapis_auth_user_consent_return} -Future obtainAccessCredentialsViaUserConsentManual( - AuthEndpoints authEndpoints, - ClientId clientId, - List scopes, - Client client, - PromptUserForConsentManual userPrompt, { - String? hostedDomain, -}) => - AuthorizationCodeGrantManualFlow( - authEndpoints, - clientId, - scopes, - client, - userPrompt, - hostedDomain: hostedDomain, - ).run(); diff --git a/third_party/googleapis_auth/lib/googleapis_auth.dart b/third_party/googleapis_auth/lib/googleapis_auth.dart deleted file mode 100644 index cc992d5d..00000000 --- a/third_party/googleapis_auth/lib/googleapis_auth.dart +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2021, the Dart 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. - -// ignore_for_file: comment_references - -/// Contains common libraries used across the package. -/// -/// In most cases, you'll want to import either -/// [auth_io] or [auth_browser] depending on your platform. -/// {@canonicalFor access_credentials.AccessCredentials} -/// {@canonicalFor access_token.AccessToken} -/// {@canonicalFor auth_client.AuthClient} -/// {@canonicalFor auth_client.AutoRefreshingAuthClient} -/// {@canonicalFor auth_functions.authenticatedClient} -/// {@canonicalFor auth_functions.autoRefreshingClient} -/// {@canonicalFor auth_functions.clientViaApiKey} -/// {@canonicalFor auth_functions.refreshCredentials} -/// {@canonicalFor client_id.ClientId} -/// {@canonicalFor exceptions.AccessDeniedException} -/// {@canonicalFor exceptions.ServerRequestFailedException} -/// {@canonicalFor exceptions.RefreshFailedException} -/// {@canonicalFor exceptions.UserConsentException} -/// {@canonicalFor response_type.ResponseType} -/// {@canonicalFor service_account_credentials.ServiceAccountCredentials} -library googleapis_auth; - -export 'src/auth_client.dart'; -export 'src/auth_functions.dart'; -export 'src/auth_endpoints.dart'; -export 'src/client_id.dart'; -export 'src/exceptions.dart'; -export 'src/response_type.dart'; -export 'src/service_account_credentials.dart'; diff --git a/third_party/googleapis_auth/lib/src/access_credentials.dart b/third_party/googleapis_auth/lib/src/access_credentials.dart deleted file mode 100644 index 1c7d8a57..00000000 --- a/third_party/googleapis_auth/lib/src/access_credentials.dart +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2021, the Dart 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 'access_token.dart'; - -/// OAuth2 Credentials. -class AccessCredentials { - /// An access token. - final AccessToken accessToken; - - /// A refresh token, which can be used to refresh the access credentials. - final String? refreshToken; - - /// A JWT used in calls to Google APIs that accept an id_token param. - final String? idToken; - - /// Scopes these credentials are valid for. - final List scopes; - - AccessCredentials( - this.accessToken, - this.refreshToken, - this.scopes, { - this.idToken, - }); - - factory AccessCredentials.fromJson(Map json) => - AccessCredentials( - AccessToken.fromJson(json['accessToken'] as Map), - json['refreshToken'] as String?, - (json['scopes'] as List).map((e) => e as String).toList(), - idToken: json['idToken'] as String?, - ); - - Map toJson() => { - 'accessToken': accessToken, - if (refreshToken != null) 'refreshToken': refreshToken, - 'idToken': idToken, - 'scopes': scopes, - }; -} diff --git a/third_party/googleapis_auth/lib/src/access_token.dart b/third_party/googleapis_auth/lib/src/access_token.dart deleted file mode 100644 index 16a74ba7..00000000 --- a/third_party/googleapis_auth/lib/src/access_token.dart +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2021, the Dart 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. - -/// An OAuth2 access token. -class AccessToken { - /// The token type, usually "Bearer" - final String type; - - /// The access token data. - final String data; - - /// Time at which the token will be expired (UTC time) - final DateTime expiry; - - /// [expiry] must be a UTC `DateTime`. - AccessToken(this.type, this.data, this.expiry) { - if (!expiry.isUtc) { - throw ArgumentError.value( - expiry, - 'expiry', - 'The expiry date must be a Utc DateTime.', - ); - } - } - - factory AccessToken.fromJson(Map json) => AccessToken( - json['type'] as String, - json['data'] as String, - DateTime.parse(json['expiry'] as String), - ); - - bool get hasExpired => DateTime.now().toUtc().isAfter(expiry); - - @override - String toString() => 'AccessToken(type=$type, data=$data, expiry=$expiry)'; - - Map toJson() => { - 'type': type, - 'data': data, - 'expiry': expiry.toIso8601String(), - }; -} diff --git a/third_party/googleapis_auth/lib/src/adc_utils.dart b/third_party/googleapis_auth/lib/src/adc_utils.dart deleted file mode 100644 index a94fc1da..00000000 --- a/third_party/googleapis_auth/lib/src/adc_utils.dart +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2021, the Dart 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:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart'; - -import 'auth_http_utils.dart'; - -Future fromApplicationsCredentialsFile( - File file, - AuthEndpoints authEndpoints, - String fileSource, - List scopes, - Client baseClient, -) async { - Object? credentials; - try { - credentials = json.decode(await file.readAsString()); - } on IOException { - throw Exception( - 'Failed to read credentials file from $fileSource', - ); - } on FormatException { - throw Exception( - 'Failed to parse JSON from credentials file from $fileSource', - ); - } - - if (credentials is Map && credentials['type'] == 'authorized_user') { - final clientId = ClientId( - credentials['client_id'] as String, - credentials['client_secret'] as String?, - ); - return AutoRefreshingClient( - baseClient, - authEndpoints, - clientId, - await refreshCredentials( - authEndpoints, - clientId, - AccessCredentials( - // Hack: Create empty credentials that have expired. - AccessToken('Bearer', '', DateTime(0).toUtc()), - credentials['refresh_token'] as String?, - scopes, - ), - baseClient, - ), - quotaProject: credentials['quota_project_id'] as String?, - ); - } - return await clientViaServiceAccount( - ServiceAccountCredentials.fromJson(credentials), - scopes, - baseClient: baseClient, - ); -} diff --git a/third_party/googleapis_auth/lib/src/auth_client.dart b/third_party/googleapis_auth/lib/src/auth_client.dart deleted file mode 100644 index a581bf2a..00000000 --- a/third_party/googleapis_auth/lib/src/auth_client.dart +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2021, the Dart 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 'package:http/http.dart'; - -import 'access_credentials.dart'; - -/// A authenticated HTTP client. -abstract class AuthClient implements Client { - /// The credentials currently used for making HTTP requests. - AccessCredentials get credentials; -} - -/// A auto-refreshing, authenticated HTTP client. -abstract class AutoRefreshingAuthClient implements AuthClient { - /// A broadcast stream of [AccessCredentials]. - /// - /// A listener will get notified when new [AccessCredentials] were obtained. - Stream get credentialUpdates; -} diff --git a/third_party/googleapis_auth/lib/src/auth_endpoints.dart b/third_party/googleapis_auth/lib/src/auth_endpoints.dart deleted file mode 100644 index 3be34b29..00000000 --- a/third_party/googleapis_auth/lib/src/auth_endpoints.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'known_uris.dart'; - -abstract class AuthEndpoints { - Uri get authorizationEndpoint; - Uri get tokenEndpoint; -} - -class GoogleAuthEndpoints extends AuthEndpoints { - @override - Uri get authorizationEndpoint => googleOauth2AuthorizationEndpoint; - - @override - Uri get tokenEndpoint => googleOauth2TokenEndpoint; -} diff --git a/third_party/googleapis_auth/lib/src/auth_functions.dart b/third_party/googleapis_auth/lib/src/auth_functions.dart deleted file mode 100644 index 63d6cc1a..00000000 --- a/third_party/googleapis_auth/lib/src/auth_functions.dart +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; - -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart'; - -import 'auth_http_utils.dart'; -import 'http_client_base.dart'; -import 'utils.dart'; - -/// Obtains a [Client] which uses the given [apiKey] for making HTTP -/// requests. -/// -/// {@macro googleapis_auth_baseClient_param} -/// -/// Note that the returned client should *only* be used for making HTTP requests -/// to Google Services. The [apiKey] should not be disclosed to third parties. -/// -/// {@template googleapis_auth_close_the_client} -/// The user is responsible for closing the returned HTTP [Client]. -/// {@endtemplate} -/// {@template googleapis_auth_not_close_the_baseClient} -/// Closing the returned [Client] will not close [baseClient]. -/// {@endtemplate} -Client clientViaApiKey( - String apiKey, { - Client? baseClient, -}) { - if (baseClient == null) { - baseClient = Client(); - } else { - baseClient = nonClosingClient(baseClient); - } - return ApiKeyClient(baseClient, apiKey); -} - -/// Obtain a [Client] which automatically authenticates requests using -/// [credentials]. -/// -/// Note that the returned [AuthClient] will not auto-refresh the given -/// [credentials]. -/// -/// {@macro googleapis_auth_close_the_client} -/// -/// If [closeUnderlyingClient] is `true`, [AuthClient.close] will also close -/// [baseClient]. -/// -/// {@macro googleapis_auth_not_close_the_baseClient} -AuthClient authenticatedClient( - Client baseClient, - AccessCredentials credentials, { - bool closeUnderlyingClient = false, -}) { - if (credentials.accessToken.type != 'Bearer') { - throw ArgumentError('Only Bearer access tokens are accepted.'); - } - return AuthenticatedClient( - baseClient, - credentials, - closeUnderlyingClient: closeUnderlyingClient, - ); -} - -/// Creates an [AutoRefreshingAuthClient] which automatically refreshes -/// [credentials] before they expire. -/// -/// {@template googleapis_auth_clientId_param} -/// The [clientId] that you obtain from the API Console -/// [Credentials page](https://console.developers.google.com/apis/credentials), -/// as described in -/// [Obtain OAuth 2.0 credentials](https://developers.google.com/identity/protocols/oauth2/openid-connect#getcredentials). -/// {@endtemplate} -/// -/// Uses [baseClient] to make authenticated HTTP requests and to refresh -/// [credentials]. -/// -/// {@macro googleapis_auth_close_the_client} -/// {@macro googleapis_auth_not_close_the_baseClient} -AutoRefreshingAuthClient autoRefreshingClient( - AuthEndpoints authEndpoints, - ClientId clientId, - AccessCredentials credentials, - Client baseClient, -) { - if (credentials.accessToken.type != 'Bearer') { - throw ArgumentError('Only Bearer access tokens are accepted.'); - } - if (credentials.refreshToken == null) { - throw ArgumentError('Refresh token in AccessCredentials was `null`.'); - } - return AutoRefreshingClient(baseClient, authEndpoints, clientId, credentials); -} - -/// Obtains refreshed [AccessCredentials] for [clientId] and [credentials]. -/// -/// {@macro googleapis_auth_clientId_param} -/// -/// {@macro googleapis_auth_client_for_creds} -Future refreshCredentials( - AuthEndpoints authEndpoints, - ClientId clientId, - AccessCredentials credentials, - Client client, -) async { - final refreshToken = credentials.refreshToken; - if (refreshToken == null) { - throw ArgumentError('clientId.refreshToken cannot be null.'); - } - - // https://developers.google.com/identity/protocols/oauth2/native-app#offline - final jsonMap = await client.oauthTokenRequest( - { - 'client_id': clientId.identifier, - // Not all providers require a client secret, - // e.g. https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow#refresh-the-access-token - if (clientId.secret != null) 'client_secret': clientId.secret!, - 'refresh_token': refreshToken, - 'grant_type': 'refresh_token', - }, - authEndpoints: authEndpoints, - ); - - final accessToken = parseAccessToken(jsonMap); - - final idToken = jsonMap['id_token'] as String?; - - return AccessCredentials( - accessToken, - credentials.refreshToken, - credentials.scopes, - idToken: idToken, - ); -} diff --git a/third_party/googleapis_auth/lib/src/auth_http_utils.dart b/third_party/googleapis_auth/lib/src/auth_http_utils.dart deleted file mode 100644 index 2e410d6c..00000000 --- a/third_party/googleapis_auth/lib/src/auth_http_utils.dart +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; - -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart'; - -import 'http_client_base.dart'; - -/// Will close the underlying `http.Client` depending on a constructor argument. -class AuthenticatedClient extends DelegatingClient implements AuthClient { - @override - final AccessCredentials credentials; - final String? quotaProject; - - AuthenticatedClient( - super.client, - this.credentials, { - this.quotaProject, - super.closeUnderlyingClient = false, - }); - - @override - Future send(BaseRequest request) async { - // Make new request object and perform the authenticated request. - final modifiedRequest = - RequestImpl(request.method, request.url, request.finalize()); - modifiedRequest.headers.addAll(request.headers); - modifiedRequest.headers['Authorization'] = - 'Bearer ${credentials.accessToken.data}'; - if (quotaProject != null) { - modifiedRequest.headers['X-Goog-User-Project'] = quotaProject!; - } - final response = await baseClient.send(modifiedRequest); - final wwwAuthenticate = response.headers['www-authenticate']; - if (wwwAuthenticate != null) { - await response.stream.drain(); - throw AccessDeniedException( - 'Access was denied ' - '(www-authenticate header was: $wwwAuthenticate).', - ); - } - return response; - } -} - -/// Adds 'key' query parameter when making HTTP requests. -/// -/// If 'key' is already present on the URI, it will complete with an exception. -/// This will prevent accidental overrides of a query parameter with the API -/// key. -class ApiKeyClient extends DelegatingClient { - final String _encodedApiKey; - - ApiKeyClient(super.client, String apiKey) - : _encodedApiKey = Uri.encodeQueryComponent(apiKey), - super(closeUnderlyingClient: true); - - @override - Future send(BaseRequest request) async { - var url = request.url; - if (url.queryParameters.containsKey('key')) { - throw ArgumentError( - 'Tried to make a HTTP request which has already a "key" query ' - 'parameter. Adding the API key would override that existing value.', - ); - } - - if (url.query == '') { - url = url.replace(query: 'key=$_encodedApiKey'); - } else { - url = url.replace(query: '${url.query}&key=$_encodedApiKey'); - } - - final modifiedRequest = RequestImpl(request.method, url, request.finalize()) - ..headers.addAll(request.headers); - return baseClient.send(modifiedRequest); - } -} - -/// Will close the underlying `http.Client` depending on a constructor argument. -class AutoRefreshingClient extends AutoRefreshDelegatingClient { - final ClientId clientId; - final String? quotaProject; - @override - AccessCredentials credentials; - late Client authClient; - final AuthEndpoints authEndpoints; - - AutoRefreshingClient( - super.client, - this.authEndpoints, - this.clientId, - this.credentials, { - super.closeUnderlyingClient, - this.quotaProject, - }) : assert(credentials.accessToken.type == 'Bearer'), - assert(credentials.refreshToken != null) { - authClient = AuthenticatedClient( - baseClient, - credentials, - quotaProject: quotaProject, - ); - } - - @override - Future send(BaseRequest request) async { - if (!credentials.accessToken.hasExpired) { - // TODO: Can this return a "access token expired" message? - // If so, we should handle it. - return authClient.send(request); - } else { - final cred = await refreshCredentials( - authEndpoints, - clientId, - credentials, - baseClient, - ); - notifyAboutNewCredentials(cred); - credentials = cred; - authClient = AuthenticatedClient( - baseClient, - cred, - quotaProject: quotaProject, - ); - return authClient.send(request); - } - } -} - -abstract class AutoRefreshDelegatingClient extends DelegatingClient - implements AutoRefreshingAuthClient { - final StreamController _credentialStreamController = - StreamController.broadcast(sync: true); - - AutoRefreshDelegatingClient( - super.client, { - super.closeUnderlyingClient, - }); - - @override - Stream get credentialUpdates => - _credentialStreamController.stream; - - void notifyAboutNewCredentials(AccessCredentials credentials) { - _credentialStreamController.add(credentials); - } - - @override - void close() { - _credentialStreamController.close(); - super.close(); - } -} diff --git a/third_party/googleapis_auth/lib/src/authentication_exception.dart b/third_party/googleapis_auth/lib/src/authentication_exception.dart deleted file mode 100644 index 1973ed2d..00000000 --- a/third_party/googleapis_auth/lib/src/authentication_exception.dart +++ /dev/null @@ -1,22 +0,0 @@ -/// Exception thrown when authentication fails. -class AuthenticationException implements Exception { - final String error; - - /// Human-readable ASCII text providing additional information, used to assist - /// the client developer in understanding the error that occurred. - final String? errorDescription; - - /// A URI identifying a human-readable web page with information about the - /// error, used to provide the client developer with additional information - /// about the error. - final String? errorUri; - - AuthenticationException( - this.error, { - this.errorDescription, - this.errorUri, - }); - - @override - String toString() => 'AuthenticationException: $error'; -} diff --git a/third_party/googleapis_auth/lib/src/browser_utils.dart b/third_party/googleapis_auth/lib/src/browser_utils.dart deleted file mode 100644 index 8d749df7..00000000 --- a/third_party/googleapis_auth/lib/src/browser_utils.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'dart:async'; -import 'dart:html' as html; -import 'dart:js' as js; - -import 'authentication_exception.dart'; - -Future initializeScript( - String scriptUrl, { - String? onloadParam, -}) async { - final instance = _ScriptLoader._instances.putIfAbsent( - scriptUrl, - () => _ScriptLoader._(scriptUrl, onloadParam: onloadParam), - ); - - await instance._initialize(); -} - -/// Creates a script that will run properly when strict CSP is enforced. -/// -/// More specifically, the script has the correct `nonce` value set. -final html.ScriptElement Function() _createScriptTag = (() { - final nonce = _getNonce(); - if (nonce == null) return html.ScriptElement.new; - - return () => html.ScriptElement()..nonce = nonce; -})(); - -/// Returns CSP nonce, if set for any script tag. -String? _getNonce({html.Window? window}) { - final currentWindow = window ?? html.window; - final elements = currentWindow.document.querySelectorAll('script'); - for (final element in elements) { - final nonceValue = - (element as html.HtmlElement).nonce ?? element.attributes['nonce']; - if (nonceValue != null && _noncePattern.hasMatch(nonceValue)) { - return nonceValue; - } - } - return null; -} - -// According to the CSP3 spec a nonce must be a valid base64 string. -// https://w3c.github.io/webappsec-csp/#grammardef-base64-value -final _noncePattern = RegExp('^[\\w+/_-]+[=]{0,2}\$'); - -const callbackTimeout = Duration(seconds: 20); - -class _ScriptLoader { - _ScriptLoader._( - this.url, { - this.onloadParam, - }); - - static final _instances = {}; - - final String url; - final String? onloadParam; - - Future? _pendingInitialization; - - Future _initialize() { - if (_pendingInitialization != null) { - return _pendingInitialization!; - } - - final completer = Completer(); - - final timeout = Timer(callbackTimeout, () { - _pendingInitialization = null; - completer.completeError( - AuthenticationException( - 'Timed out while waiting for library to load: $url', - ), - ); - }); - - void loadComplete() { - timeout.cancel(); - completer.complete(); - } - - final loadFunctionName = '_dartScriptLoad_${url.hashCode}'; - - js.context[loadFunctionName] = loadComplete; - final fullUrl = - onloadParam == null ? url : '$url?${onloadParam!}=$loadFunctionName'; - - final script = _createScriptTag() - ..async = true - ..defer = true - ..src = fullUrl; - if (onloadParam == null) { - script.onLoad.first.then((event) { - loadComplete(); - }); - } - script.onError.first.then((errorEvent) { - timeout.cancel(); - _pendingInitialization = null; - if (!completer.isCompleted) { - // script loading errors can still happen after timeouts - completer.completeError( - AuthenticationException('Failed to load library: $url')); - } - }); - html.document.body!.append(script); - - _pendingInitialization = completer.future; - return completer.future; - } -} diff --git a/third_party/googleapis_auth/lib/src/client_id.dart b/third_party/googleapis_auth/lib/src/client_id.dart deleted file mode 100644 index 4652708e..00000000 --- a/third_party/googleapis_auth/lib/src/client_id.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2021, the Dart 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. - -/// Represents the client application's credentials. -class ClientId { - /// The client ID that you obtain from the API Console - /// [Credentials page](https://console.developers.google.com/apis/credentials), - /// as described in - /// [Obtain OAuth 2.0 credentials](https://developers.google.com/identity/protocols/oauth2/openid-connect#getcredentials). - final String identifier; - - /// The client secret used to identify this application to the server. - final String? secret; - - ClientId(this.identifier, [this.secret]); - - ClientId.serviceAccount(this.identifier) : secret = null; - - factory ClientId.fromJson(Map json) => ClientId( - json['identifier'] as String, - json['secret'] as String?, - ); - - Map toJson() => { - 'identifier': identifier, - if (secret != null) 'secret': secret, - }; -} diff --git a/third_party/googleapis_auth/lib/src/crypto/asn1.dart b/third_party/googleapis_auth/lib/src/crypto/asn1.dart deleted file mode 100644 index f28b3cf1..00000000 --- a/third_party/googleapis_auth/lib/src/crypto/asn1.dart +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2014, the Dart 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:typed_data'; - -import 'rsa.dart'; - -// ignore: avoid_classes_with_only_static_members -class ASN1Parser { - static const integerTag = 0x02; - static const octetStringTag = 0x04; - static const nullTag = 0x05; - static const objectIdTag = 0x06; - static const sequenceTag = 0x30; - - static ASN1Object parse(Uint8List bytes) { - Never invalidFormat(String msg) { - throw ArgumentError('Invalid DER encoding: $msg'); - } - - final data = ByteData.view(bytes.buffer); - var offset = 0; - final end = bytes.length; - - void checkNBytesAvailable(int n) { - if ((offset + n) > end) { - invalidFormat('Tried to read more bytes than available.'); - } - } - - List readBytes(int n) { - checkNBytesAvailable(n); - - final integerBytes = bytes.sublist(offset, offset + n); - offset += n; - return integerBytes; - } - - int readEncodedLength() { - checkNBytesAvailable(1); - - final lengthByte = data.getUint8(offset++); - - // Short length encoding form: This byte is the length itself. - if (lengthByte < 0x80) { - return lengthByte; - } - - // Long length encoding form: - // This byte has in bits 0..6 the number of bytes following which encode - // the length. - var countLengthBytes = lengthByte & 0x7f; - checkNBytesAvailable(countLengthBytes); - - var length = 0; - while (countLengthBytes > 0) { - length = (length << 8) | data.getUint8(offset++); - countLengthBytes--; - } - return length; - } - - void readNullBytes() { - checkNBytesAvailable(1); - final nullByte = data.getUint8(offset++); - if (nullByte != 0x00) { - invalidFormat('Null byte expect, but was: $nullByte.'); - } - } - - ASN1Object decodeObject() { - checkNBytesAvailable(1); - final tag = bytes[offset++]; - switch (tag) { - case integerTag: - final size = readEncodedLength(); - return ASN1Integer(RSAAlgorithm.bytes2BigInt(readBytes(size))); - case octetStringTag: - final size = readEncodedLength(); - return ASN1OctetString(readBytes(size)); - case nullTag: - readNullBytes(); - return ASN1Null(); - case objectIdTag: - final size = readEncodedLength(); - return ASN1ObjectIdentifier(readBytes(size)); - case sequenceTag: - final lengthInBytes = readEncodedLength(); - if ((offset + lengthInBytes) > end) { - invalidFormat('Tried to read more bytes than available.'); - } - final endOfSequence = offset + lengthInBytes; - - final objects = []; - while (offset < endOfSequence) { - objects.add(decodeObject()); - } - return ASN1Sequence(objects); - default: - invalidFormat( - 'Unexpected tag $tag at offset ${offset - 1} (end: $end).', - ); - } - } - - final obj = decodeObject(); - if (offset != bytes.length) { - throw ArgumentError('More bytes than expected in ASN1 encoding.'); - } - return obj; - } -} - -abstract class ASN1Object {} - -class ASN1Sequence extends ASN1Object { - final List objects; - - ASN1Sequence(this.objects); -} - -class ASN1Integer extends ASN1Object { - final BigInt integer; - - ASN1Integer(this.integer); -} - -class ASN1OctetString extends ASN1Object { - final List bytes; - - ASN1OctetString(this.bytes); -} - -class ASN1ObjectIdentifier extends ASN1Object { - final List bytes; - - ASN1ObjectIdentifier(this.bytes); -} - -class ASN1Null extends ASN1Object {} diff --git a/third_party/googleapis_auth/lib/src/crypto/pem.dart b/third_party/googleapis_auth/lib/src/crypto/pem.dart deleted file mode 100644 index b000796a..00000000 --- a/third_party/googleapis_auth/lib/src/crypto/pem.dart +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2014, the Dart 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:convert'; -import 'dart:typed_data'; - -import 'asn1.dart'; -import 'rsa.dart'; - -/// Decode a [RSAPrivateKey] from the string content of a PEM file. -/// -/// A PEM file can be extracted from a .p12 cryptostore with -/// $ openssl pkcs12 -nocerts -nodes -passin pass:notasecret \ -/// -in *-privatekey.p12 -out *-privatekey.pem -RSAPrivateKey keyFromString(String pemFileString) { - final bytes = _getBytesFromPEMString(pemFileString); - return _extractRSAKeyFromDERBytes(bytes); -} - -/// Helper function for decoding the base64 in [pemString]. -Uint8List _getBytesFromPEMString(String pemString) { - final lines = LineSplitter.split(pemString) - .map((line) => line.trim()) - .where((line) => line.isNotEmpty) - .toList(); - - if (lines.length < 2 || - !lines.first.startsWith('-----BEGIN') || - !lines.last.startsWith('-----END')) { - throw ArgumentError( - 'The given string does not have the correct ' - 'begin/end markers expected in a PEM file.', - ); - } - final base64 = lines.sublist(1, lines.length - 1).join(); - return Uint8List.fromList(base64Decode(base64)); -} - -/// Helper to decode the ASN.1/DER bytes in [bytes] into an [RSAPrivateKey]. -RSAPrivateKey _extractRSAKeyFromDERBytes(Uint8List bytes) { - // We recognize two formats: - // Real format: - // - // PrivateKey := seq[int/version=0, int/n, int/e, int/d, int/p, - // int/q, int/dmp1, int/dmq1, int/coeff] - // - // Or the above `PrivateKey` embeddded inside another ASN object: - // Encapsulated := seq[int/version=0, - // seq[obj-id/rsa-id, null-obj], - // octet-string/PrivateKey] - // - - RSAPrivateKey privateKeyFromSequence(ASN1Sequence asnSequence) { - final objects = asnSequence.objects; - - final asnIntegers = objects.take(9).map((o) => o as ASN1Integer).toList(); - - final version = asnIntegers.first; - if (version.integer != BigInt.zero) { - throw ArgumentError('Expected version 0, got: ${version.integer}.'); - } - - final key = RSAPrivateKey( - asnIntegers[1].integer, - asnIntegers[2].integer, - asnIntegers[3].integer, - asnIntegers[4].integer, - asnIntegers[5].integer, - asnIntegers[6].integer, - asnIntegers[7].integer, - asnIntegers[8].integer, - ); - - final bitLength = key.bitLength; - if (bitLength != 1024 && bitLength != 2048 && bitLength != 4096) { - throw ArgumentError( - 'The RSA modulus has a bit length of $bitLength. ' - 'Only 1024, 2048 and 4096 are supported.', - ); - } - return key; - } - - try { - final asn = ASN1Parser.parse(bytes); - if (asn is ASN1Sequence) { - final objects = asn.objects; - if (objects.length == 3 && objects[2] is ASN1OctetString) { - final string = objects[2] as ASN1OctetString; - // Seems like the embedded form. - // TODO: Validate that rsa identifier matches! - return privateKeyFromSequence( - ASN1Parser.parse(string.bytes as Uint8List) as ASN1Sequence, - ); - } - } - return privateKeyFromSequence(asn as ASN1Sequence); - } catch (error) { - throw ArgumentError( - 'Error while extracting private key from DER bytes: $error', - ); - } -} diff --git a/third_party/googleapis_auth/lib/src/crypto/rsa.dart b/third_party/googleapis_auth/lib/src/crypto/rsa.dart deleted file mode 100644 index 0d00be44..00000000 --- a/third_party/googleapis_auth/lib/src/crypto/rsa.dart +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -// A small part is based on a JavaScript implementation of RSA by Tom Wu -// but re-written in dart. - -import 'dart:typed_data'; - -/// Represents integers obtained while creating a Public/Private key pair. -class RSAPrivateKey { - /// First prime number. - final BigInt p; - - /// Second prime number. - final BigInt q; - - /// Modulus for public and private keys. Satisfies `n=p*q`. - final BigInt n; - - /// Public key exponent. Satisfies `d*e=1 mod phi(n)`. - final BigInt e; - - /// Private key exponent. Satisfies `d*e=1 mod phi(n)`. - final BigInt d; - - /// Different form of [p]. Satisfies `dmp1=d mod (p-1)`. - final BigInt dmp1; - - /// Different form of [p]. Satisfies `dmq1=d mod (q-1)`. - final BigInt dmq1; - - /// A coefficient which satisfies `coeff=q^-1 mod p`. - final BigInt coeff; - - /// The number of bits used for the modulus. Usually 1024, 2048 or 4096 bits. - int get bitLength => n.bitLength; - - RSAPrivateKey( - this.n, - this.e, - this.d, - this.p, - this.q, - this.dmp1, - this.dmq1, - this.coeff, - ); -} - -// ignore: avoid_classes_with_only_static_members -/// Provides a [encrypt] method for encrypting messages with a [RSAPrivateKey]. -abstract class RSAAlgorithm { - /// Performs the encryption of [bytes] with the private [key]. - /// Others who have access to the public key will be able to decrypt this - /// the result. - /// - /// The [intendedLength] argument specifies the number of bytes in which the - /// result should be encoded. Zero bytes will be used for padding. - static List encrypt( - RSAPrivateKey key, - List bytes, - int intendedLength, - ) { - final message = bytes2BigInt(bytes); - final encryptedMessage = _encryptInteger(key, message); - return integer2Bytes(encryptedMessage, intendedLength); - } - - static BigInt _encryptInteger(RSAPrivateKey key, BigInt x) { - // The following is equivalent to `_modPow(x, key.d, key.n) but is much - // more efficient. It exploits the fact that we have dmp1/dmq1. - var xp = _modPow(x % key.p, key.dmp1, key.p); - final xq = _modPow(x % key.q, key.dmq1, key.q); - while (xp < xq) { - xp += key.p; - } - return ((((xp - xq) * key.coeff) % key.p) * key.q) + xq; - } - - static BigInt _modPow(BigInt b, BigInt e, BigInt m) { - if (e < BigInt.one) { - return BigInt.one; - } - if (b < BigInt.zero || b > m) { - b = b % m; - } - var r = BigInt.one; - while (e > BigInt.zero) { - if ((e & BigInt.one) > BigInt.zero) { - r = (r * b) % m; - } - e >>= 1; - b = (b * b) % m; - } - return r; - } - - static BigInt bytes2BigInt(List bytes) { - var number = BigInt.zero; - for (var i = 0; i < bytes.length; i++) { - number = (number << 8) | BigInt.from(bytes[i]); - } - return number; - } - - static List integer2Bytes(BigInt integer, int intendedLength) { - if (integer < BigInt.one) { - throw ArgumentError('Only positive integers are supported.'); - } - final bytes = Uint8List(intendedLength); - for (var i = bytes.length - 1; i >= 0; i--) { - bytes[i] = (integer & _bigIntFF).toInt(); - integer >>= 8; - } - return bytes; - } -} - -final _bigIntFF = BigInt.from(0xff); diff --git a/third_party/googleapis_auth/lib/src/crypto/rsa_sign.dart b/third_party/googleapis_auth/lib/src/crypto/rsa_sign.dart deleted file mode 100644 index a8ac471b..00000000 --- a/third_party/googleapis_auth/lib/src/crypto/rsa_sign.dart +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2014, the Dart 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:typed_data'; - -import 'package:crypto/crypto.dart'; - -import 'asn1.dart'; -import 'rsa.dart'; - -/// Used for signing messages with a private RSA key. -/// -/// The implemented algorithm can be seen in -/// RFC 3447, Section 9.2 EMSA-PKCS1-v1_5. -class RS256Signer { - // NIST sha-256 OID (2 16 840 1 101 3 4 2 1) - // See a reference for the encoding here: - // http://msdn.microsoft.com/en-us/library/bb540809%28v=vs.85%29.aspx - static const _rsaSha256AlgorithmIdentifier = [ - 0x06, - 0x09, - 0x60, - 0x86, - 0x48, - 0x01, - 0x65, - 0x03, - 0x04, - 0x02, - 0x01 - ]; - - final RSAPrivateKey _rsaKey; - - RS256Signer(this._rsaKey); - - List sign(List bytes) { - final digest = _digestInfo(sha256.convert(bytes).bytes); - final modulusLen = (_rsaKey.bitLength + 7) ~/ 8; - - final block = Uint8List(modulusLen); - final padLength = block.length - digest.length - 3; - block[0] = 0x00; - block[1] = 0x01; - block.fillRange(2, 2 + padLength, 0xFF); - block[2 + padLength] = 0x00; - block.setRange(2 + padLength + 1, block.length, digest); - return RSAAlgorithm.encrypt(_rsaKey, block, modulusLen); - } - - static Uint8List _digestInfo(List hash) { - // DigestInfo :== SEQUENCE { - // digestAlgorithm AlgorithmIdentifier, - // digest OCTET STRING - // } - var offset = 0; - final digestInfo = Uint8List( - 2 + 2 + _rsaSha256AlgorithmIdentifier.length + 2 + 2 + hash.length, - ); - { - // DigestInfo - digestInfo[offset++] = ASN1Parser.sequenceTag; - digestInfo[offset++] = digestInfo.length - 2; - { - // AlgorithmIdentifier. - digestInfo[offset++] = ASN1Parser.sequenceTag; - digestInfo[offset++] = _rsaSha256AlgorithmIdentifier.length + 2; - digestInfo.setAll(offset, _rsaSha256AlgorithmIdentifier); - offset += _rsaSha256AlgorithmIdentifier.length; - digestInfo[offset++] = ASN1Parser.nullTag; - digestInfo[offset++] = 0; - } - digestInfo[offset++] = ASN1Parser.octetStringTag; - digestInfo[offset++] = hash.length; - digestInfo.setAll(offset, hash); - } - return digestInfo; - } -} diff --git a/third_party/googleapis_auth/lib/src/exceptions.dart b/third_party/googleapis_auth/lib/src/exceptions.dart deleted file mode 100644 index c90c1108..00000000 --- a/third_party/googleapis_auth/lib/src/exceptions.dart +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2021, the Dart 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. - -/// No longer used. Replaced by [ServerRequestFailedException]. -@Deprecated('No longer used. Replaced by ServerRequestFailedException.') -typedef RefreshFailedException = ServerRequestFailedException; - -/// Thrown if an attempt to make an authorized request failed. -class AccessDeniedException implements Exception { - final String message; - - AccessDeniedException(this.message); - - @override - String toString() => message; -} - -/// Thrown if user did not give their consent. -class UserConsentException implements Exception { - final String message; - - final String? details; - - UserConsentException(this.message, {this.details}); - - @override - String toString() => [message, if (details != null) details].join(' '); -} - -/// Thrown when a request to or the response from an authentication service is -/// invalid. -/// -/// This could indicate invalid credentials. -class ServerRequestFailedException implements Exception { - /// Describes the failure. - final String message; - - /// The HTTP status code of the response, if known. - /// - /// If `null`, the status code was likely `200` and there was another issue - /// with the response. - final int? statusCode; - - /// Data representing the content of the response, if any. - /// - /// This may be a [String] representing the raw content of the response or - /// the a parsed JSON literal of the content. - final Object? responseContent; - - ServerRequestFailedException( - this.message, { - this.statusCode, - required this.responseContent, - }); - - @override - String toString() => - [message, if (statusCode != null) 'Status code: $statusCode'].join(' '); -} diff --git a/third_party/googleapis_auth/lib/src/http_client_base.dart b/third_party/googleapis_auth/lib/src/http_client_base.dart deleted file mode 100644 index 84e37c98..00000000 --- a/third_party/googleapis_auth/lib/src/http_client_base.dart +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; - -import 'package:http/http.dart'; - -/// Base class for delegating HTTP clients. -/// -/// If [closeUnderlyingClient] is `true`, [close] will also close [baseClient]. -abstract class DelegatingClient extends BaseClient { - final Client baseClient; - final bool closeUnderlyingClient; - bool _isClosed = false; - - DelegatingClient(this.baseClient, {this.closeUnderlyingClient = true}); - - @override - void close() { - if (_isClosed) { - throw StateError('Cannot close a HTTP client more than once.'); - } - _isClosed = true; - super.close(); - - if (closeUnderlyingClient) { - baseClient.close(); - } - } -} - -/// A reference counted HTTP client. -/// -/// It uses a base [Client] which will be closed once the reference count -/// reaches zero. The initial reference count is one, since the caller has a -/// reference to the constructed instance. -class RefCountedClient extends DelegatingClient { - int _refCount; - - RefCountedClient(super.baseClient, {int initialRefCount = 1}) - : _refCount = initialRefCount, - super(closeUnderlyingClient: true); - - @override - Future send(BaseRequest request) { - _ensureClientIsOpen(); - return baseClient.send(request); - } - - /// Acquires a new reference which causes the reference count to be - /// incremented by 1. - void acquire() { - _ensureClientIsOpen(); - _refCount++; - } - - /// Releases a new reference which causes the reference count to be - /// decremented by 1. - void release() { - _ensureClientIsOpen(); - _refCount--; - - if (_refCount == 0) { - super.close(); - } - } - - /// Is equivalent to calling `release`. - @override - void close() { - release(); - } - - void _ensureClientIsOpen() { - if (_refCount <= 0) { - throw StateError( - 'This reference counted HTTP client has reached a count of zero and ' - 'can no longer be used for making HTTP requests.', - ); - } - } -} - -// NOTE: -// Calling close on the returned client once will not close the underlying -// [baseClient]. -Client nonClosingClient(Client baseClient) => - RefCountedClient(baseClient, initialRefCount: 2); - -class RequestImpl extends BaseRequest { - final Stream> _stream; - - RequestImpl(super.method, super.url, [Stream>? stream]) - : _stream = stream ?? const Stream.empty(); - - @override - ByteStream finalize() { - super.finalize(); - return ByteStream(_stream); - } -} diff --git a/third_party/googleapis_auth/lib/src/known_uris.dart b/third_party/googleapis_auth/lib/src/known_uris.dart deleted file mode 100644 index bc73ddbe..00000000 --- a/third_party/googleapis_auth/lib/src/known_uris.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2021, the Dart 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. - -/// token_endpoint -/// via https://accounts.google.com/.well-known/openid-configuration -final googleOauth2TokenEndpoint = Uri.https('oauth2.googleapis.com', 'token'); - -/// authorization_endpoint -/// via https://accounts.google.com/.well-known/openid-configuration -final googleOauth2AuthorizationEndpoint = - Uri.https('accounts.google.com', 'o/oauth2/v2/auth'); diff --git a/third_party/googleapis_auth/lib/src/metadata_server_client.dart b/third_party/googleapis_auth/lib/src/metadata_server_client.dart deleted file mode 100644 index 847a9871..00000000 --- a/third_party/googleapis_auth/lib/src/metadata_server_client.dart +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2021, the Dart 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 'package:http/http.dart'; - -import 'access_credentials.dart'; -import 'auth_client.dart'; -import 'oauth2_flows/base_flow.dart'; -import 'oauth2_flows/metadata_server.dart'; - -/// Obtain oauth2 [AccessCredentials] using the metadata API on ComputeEngine. -/// -/// In case the VM was not configured with access to the requested scopes or an -/// error occurs the returned future will complete with an `Exception`. -/// -/// {@template googleapis_auth_client_for_creds} -/// [client] will be used for making the HTTP requests needed to create the -/// returned [AccessCredentials]. -/// {@endtemplate} -/// -/// No credentials are needed. But this function is only intended to work on a -/// Google Compute Engine VM with configured access to Google APIs. -Future obtainAccessCredentialsViaMetadataServer( - Client client, -) => - MetadataServerAuthorizationFlow(client).run(); - -/// Obtains oauth2 credentials and returns an authenticated HTTP client. -/// -/// See [obtainAccessCredentialsViaMetadataServer] for specifics about the -/// arguments used for obtaining access credentials. -/// -/// {@macro googleapis_auth_returned_auto_refresh_client} -/// -/// {@macro googleapis_auth_baseClient_param} -/// -/// {@macro googleapis_auth_close_the_client} -/// {@macro googleapis_auth_not_close_the_baseClient} -Future clientViaMetadataServer({ - Client? baseClient, -}) async => - await clientFromFlow( - MetadataServerAuthorizationFlow.new, - baseClient: baseClient, - ); diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/auth_code.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/auth_code.dart deleted file mode 100644 index 74f4eff6..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/auth_code.dart +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; -import 'dart:convert'; -import 'dart:math'; -import 'dart:typed_data'; - -import 'package:crypto/crypto.dart'; -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart' as http; - -import '../utils.dart'; - -Uri createAuthenticationUri({ - required AuthEndpoints authEndpoints, - required String redirectUri, - required String clientId, - required Iterable scopes, - required String codeVerifier, - String? hostedDomain, - String? state, - bool offline = false, -}) { - final queryValues = { - 'client_id': clientId, - 'response_type': 'code', - 'redirect_uri': redirectUri, - 'scope': scopes.join(' '), - 'code_challenge': _codeVerifierShaEncode(codeVerifier), - 'code_challenge_method': 'S256', - if (offline) 'access_type': 'offline', - if (hostedDomain != null) 'hd': hostedDomain, - if (state != null) 'state': state, - }; - return authEndpoints.authorizationEndpoint.replace( - queryParameters: queryValues, - ); -} - -/// https://developers.google.com/identity/protocols/oauth2/native-app#create-code-challenge -/// https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 -String createCodeVerifier() { - final rnd = Random.secure(); - - return List.generate(128, (index) => _safe[rnd.nextInt(_safe.length)]).join(); -} - -/// See https://developers.google.com/identity/protocols/oauth2/openid-connect#createxsrftoken -String randomState() { - final rnd = Random.secure(); - - final list = Uint32List(6); - for (var i = 0; i < list.length; i++) { - list[i] = rnd.nextInt(1 << 32); - } - - final value = base64UrlEncode(Uint8List.view(list.buffer)); - return _stripBase64Equals(value); -} - -// https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 -const _safe = '0123456789-._~' - 'abcdefghijklmnopqrstuvwxyz' - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - -String _codeVerifierShaEncode(String codeVerifier) { - final asciiBytes = ascii.encode(codeVerifier); - final sha26Bytes = sha256.convert(asciiBytes).bytes; - final output = base64UrlEncode(sha26Bytes); - return _stripBase64Equals(output); -} - -String _stripBase64Equals(String value) { - while (value.endsWith('=')) { - value = value.substring(0, value.length - 1); - } - return value; -} - -/// Obtain oauth2 [AccessCredentials] by exchanging an authorization code. -/// -/// Running a hybrid oauth2 flow as described in the -/// `googleapis_auth.auth_browser` library results in a `HybridFlowResult` which -/// contains short-lived [AccessCredentials] for the client and an authorization -/// code. This authorization code needs to be transferred to the server, which -/// can exchange it against long-lived [AccessCredentials]. -/// -/// {@macro googleapis_auth_client_for_creds} -/// -/// {@macro googleapis_auth_clientId_param} -/// -/// If the authorization code was obtained using the mentioned hybrid flow, the -/// [redirectUrl] must be `"postmessage"` (default). -/// -/// If you obtained the authorization code using a different mechanism, the -/// [redirectUrl] must be the same that was used to obtain the code. -/// -/// NOTE: Only the server application will know the `client secret` - which is -/// necessary to exchange an authorization code against access tokens. -/// -/// NOTE: It is important to transmit the authorization code in a secure manner -/// to the server. You should use "anti-request forgery state tokens" to guard -/// against "cross site request forgery" attacks. -Future obtainAccessCredentialsViaCodeExchange( - AuthEndpoints authEndpoints, - http.Client client, - ClientId clientId, - String code, { - String redirectUrl = 'postmessage', - String? codeVerifier, -}) async { - final jsonMap = await client.oauthTokenRequest( - { - 'client_id': clientId.identifier, - 'client_secret': clientId.secret ?? '', - 'code': code, - if (codeVerifier != null) 'code_verifier': codeVerifier, - 'grant_type': 'authorization_code', - 'redirect_uri': redirectUrl, - }, - authEndpoints: authEndpoints, - ); - final accessToken = parseAccessToken(jsonMap); - - final idToken = jsonMap['id_token'] as String?; - final refreshToken = jsonMap['refresh_token'] as String?; - - final scope = jsonMap['scope']; - if (scope is! String) { - throw ServerRequestFailedException( - 'The response did not include a `scope` value of type `String`.', - responseContent: json, - ); - } - final scopes = scope.split(' ').toList(); - - return AccessCredentials( - accessToken, - refreshToken, - scopes, - idToken: idToken, - ); -} - -List parseScopes(Map json) { - final scope = json['scope']; - if (scope is! String) { - throw ServerRequestFailedException( - 'The response did not include a `scope` value of type `String`.', - responseContent: json, - ); - } - return scope.split(' ').toList(); -} diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_abstract_flow.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_abstract_flow.dart deleted file mode 100644 index 4140285d..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_abstract_flow.dart +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2021, the Dart 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 'package:http/http.dart' as http; - -import '../access_credentials.dart'; -import '../auth_endpoints.dart'; -import '../client_id.dart'; -import 'auth_code.dart'; -import 'base_flow.dart'; - -abstract class AuthorizationCodeGrantAbstractFlow implements BaseFlow { - final AuthEndpoints authEndpoints; - final ClientId clientId; - final String? hostedDomain; - final List scopes; - final http.Client _client; - - AuthorizationCodeGrantAbstractFlow( - this.authEndpoints, - this.clientId, - this.scopes, - this._client, { - this.hostedDomain, - }); - - Future obtainAccessCredentialsUsingCodeImpl( - String code, - String redirectUri, { - required AuthEndpoints authEndpoints, - required String codeVerifier, - }) => - obtainAccessCredentialsViaCodeExchange( - authEndpoints, - _client, - clientId, - code, - redirectUrl: redirectUri, - codeVerifier: codeVerifier, - ); - - Uri authenticationUri( - String redirectUri, { - String? state, - required String codeVerifier, - }) => - createAuthenticationUri( - authEndpoints: authEndpoints, - redirectUri: redirectUri, - clientId: clientId.identifier, - scopes: scopes, - codeVerifier: codeVerifier, - hostedDomain: hostedDomain, - state: state, - ); -} diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_manual_flow.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_manual_flow.dart deleted file mode 100644 index f0a10eb2..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_manual_flow.dart +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2021, the Dart 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:async'; - -import '../access_credentials.dart'; -import '../typedefs.dart'; -import 'auth_code.dart'; -import 'authorization_code_grant_abstract_flow.dart'; - -/// Runs an oauth2 authorization code grant flow using manual Copy&Paste. -/// -/// This class is able to run an oauth2 authorization flow. It takes a user -/// supplied function which will be called with an URI. The user is expected -/// to navigate to that URI and to grant access to the client. -/// -/// Google will give the resource owner a code. The user supplied function needs -/// to complete with that code. -/// -/// The authorization code will then be used to obtain access credentials. -class AuthorizationCodeGrantManualFlow - extends AuthorizationCodeGrantAbstractFlow { - final PromptUserForConsentManual userPrompt; - - AuthorizationCodeGrantManualFlow( - super.authEndpoints, - super.clientId, - super.scopes, - super.client, - this.userPrompt, { - super.hostedDomain, - }); - - @override - Future run() async { - final codeVerifier = createCodeVerifier(); - - // Prompt user and wait until they goes to URL and copy&pastes the auth code - // in. - final code = await userPrompt( - authenticationUri( - _redirectionUri, - codeVerifier: codeVerifier, - ).toString(), - ); - // Use code to obtain credentials - return obtainAccessCredentialsUsingCodeImpl( - code, - _redirectionUri, - authEndpoints: authEndpoints, - codeVerifier: codeVerifier, - ); - } -} - -const _redirectionUri = 'urn:ietf:wg:oauth:2.0:oob'; diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_server_flow.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_server_flow.dart deleted file mode 100644 index a4eed091..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/authorization_code_grant_server_flow.dart +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2021, the Dart 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:async'; -import 'dart:io'; - -import '../access_credentials.dart'; -import '../exceptions.dart'; -import '../typedefs.dart'; -import 'auth_code.dart'; -import 'authorization_code_grant_abstract_flow.dart'; - -/// Runs an oauth2 authorization code grant flow using an HTTP server. -/// -/// This class is able to run an oauth2 authorization flow. It takes a user -/// supplied function which will be called with an URI. The user is expected -/// to navigate to that URI and to grant access to the client. -/// -/// Once the user has granted access to the client, Google will redirect the -/// user agent to a URL pointing to a locally running HTTP server. Which in turn -/// will be able to extract the authorization code from the URL and use it to -/// obtain access credentials. -class AuthorizationCodeGrantServerFlow - extends AuthorizationCodeGrantAbstractFlow { - final PromptUserForConsent userPrompt; - final int listenPort; - - AuthorizationCodeGrantServerFlow( - super.authEndpoints, - super.clientId, - super.scopes, - super.client, - this.userPrompt, { - super.hostedDomain, - this.listenPort = 0, - }); - - @override - Future run() async { - final server = await HttpServer.bind('localhost', listenPort); - - try { - final port = server.port; - final redirectionUri = 'http://localhost:$port'; - final state = randomState(); - final codeVerifier = createCodeVerifier(); - - // Prompt user and wait until they goes to URL and the google - // authorization server calls back to our locally running HTTP server. - userPrompt( - authenticationUri( - redirectionUri, - state: state, - codeVerifier: codeVerifier, - ).toString(), - ); - - final request = await server.first; - final uri = request.uri; - - try { - if (request.method != 'GET') { - throw Exception( - 'Invalid response from server ' - '(expected GET request callback, got: ${request.method}).', - ); - } - - final returnedState = uri.queryParameters['state']; - if (state != returnedState) { - throw Exception( - 'Invalid response from server (state did not match).', - ); - } - - final error = uri.queryParameters['error']; - if (error != null) { - throw UserConsentException( - 'Error occurred while obtaining access credentials: $error', - ); - } - - final code = uri.queryParameters['code']; - if (code == null || code.isEmpty) { - throw Exception( - 'Invalid response from server (no auth code transmitted).', - ); - } - final credentials = await obtainAccessCredentialsUsingCodeImpl( - code, - redirectionUri, - authEndpoints: authEndpoints, - codeVerifier: codeVerifier, - ); - - // TODO: We could introduce a user-defined redirect page. - request.response - ..statusCode = 200 - ..headers.set('content-type', 'text/html; charset=UTF-8') - ..write( - ''' - - - - - - Authorization successful. - - - -

Application has successfully obtained access credentials

-

This window can be closed now.

- -''', - ); - await request.response.close(); - return credentials; - } catch (e) { - request.response.statusCode = 500; - await request.response.close().catchError((_) {}); - rethrow; - } - } finally { - await server.close(); - } - } -} diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/base_flow.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/base_flow.dart deleted file mode 100644 index 69aac72f..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/base_flow.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:http/http.dart'; - -import '../access_credentials.dart'; -import '../auth_client.dart'; -import '../auth_functions.dart'; -import '../auth_http_utils.dart'; -import '../http_client_base.dart'; - -/// Base class for "Flows" that provide [AccessCredentials]. -abstract class BaseFlow { - Future run(); -} - -Future clientFromFlow( - BaseFlow Function(Client client) flowFactory, { - Client? baseClient, -}) async { - if (baseClient == null) { - baseClient = Client(); - } else { - baseClient = nonClosingClient(baseClient); - } - - final flow = flowFactory(baseClient); - - try { - final credentials = await flow.run(); - return _FlowClient(baseClient, credentials, flow); - } catch (e) { - baseClient.close(); - rethrow; - } -} - -// Will close the underlying `http.Client`. -class _FlowClient extends AutoRefreshDelegatingClient { - final BaseFlow _flow; - @override - AccessCredentials credentials; - Client _authClient; - - _FlowClient(super.client, this.credentials, this._flow) - : _authClient = authenticatedClient(client, credentials); - - @override - Future send(BaseRequest request) async { - if (credentials.accessToken.hasExpired) { - final newCredentials = await _flow.run(); - notifyAboutNewCredentials(newCredentials); - credentials = newCredentials; - _authClient = authenticatedClient(baseClient, credentials); - } - return _authClient.send(request); - } -} diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/implicit.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/implicit.dart deleted file mode 100644 index f538adaa..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/implicit.dart +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; -import 'dart:html' as html; -import 'dart:js' as js; - -import '../access_credentials.dart'; -import '../access_token.dart'; -import '../authentication_exception.dart'; -import '../browser_utils.dart'; -import '../exceptions.dart'; -import '../response_type.dart'; - -// This will be overridden by tests. -String gapiUrl = 'https://apis.google.com/js/client.js'; - -/// This class performs the implicit browser-based oauth2 flow. -/// -/// It has to be used in two steps: -/// -/// 1. First call initialize() and wait until the Future completes successfully -/// - loads the 'gapi' JavaScript library into the current document -/// - wait until the library signals it is ready -/// -/// 2. Call login() as often as needed. -/// - will call the 'gapi' JavaScript lib to trigger an oauth2 browser flow -/// => This might create a popup which asks the user for consent. -/// - will wait until the flow is completed (successfully or not) -/// => Completes with AccessToken or an Exception. -/// 3. Call loginHybrid() as often as needed. -/// - will call the 'gapi' JavaScript lib to trigger an oauth2 browser flow -/// => This might create a popup which asks the user for consent. -/// - will wait until the flow is completed (successfully or not) -/// => Completes with a tuple [AccessCredentials cred, String authCode] -/// or an Exception. -class ImplicitFlow { - final String _clientId; - final List _scopes; - final bool _enableDebugLogs; - - ImplicitFlow(this._clientId, this._scopes, this._enableDebugLogs); - - /// Readies the flow for calls to [login] by loading the 'gapi' - /// JavaScript library, or returning the [Future] of a pending - /// initialization if any object has called this method already. - Future initialize() async { - await initializeScript(gapiUrl, onloadParam: 'onload'); - if (_enableDebugLogs) _gapiAuth2.callMethod('enableDebugLogs', [true]); - } - - Future loginHybrid({ - String? prompt, - String? loginHint, - String? hostedDomain, - }) => - _login( - prompt: prompt, - responseTypes: [ResponseType.code, ResponseType.token], - loginHint: loginHint, - hostedDomain: hostedDomain, - ); - - Future login({ - String? prompt, - String? loginHint, - List? responseTypes, - String? hostedDomain, - }) async => - (await _login( - prompt: prompt, - loginHint: loginHint, - responseTypes: responseTypes, - hostedDomain: hostedDomain, - )) - .credential; - - // Completes with either credentials or a tuple of credentials and authCode. - // hybrid => [AccessCredentials credentials, String authCode] - // !hybrid => AccessCredentials - // - // Alternatively, the response types can be set directly if `hybrid` is not - // set to `true`. - Future _login({ - required String? prompt, - required String? hostedDomain, - required String? loginHint, - required List? responseTypes, - }) { - final completer = Completer(); - - // https://developers.google.com/identity/sign-in/web/reference#gapiauth2authorizeconfig - final json = { - 'client_id': _clientId, - 'scope': _scopes.join(' '), - 'response_type': responseTypes == null || responseTypes.isEmpty - ? 'token' - : responseTypes.map(_responseTypeToString).join(' '), - if (prompt != null) 'prompt': prompt, - // cookie_policy – missing - if (hostedDomain != null) 'hosted_domain': hostedDomain, - if (loginHint != null) 'login_hint': loginHint, - // include_granted_scopes - missing - 'plugin_name': 'dart-googleapis_auth', - }; - - _gapiAuth2.callMethod('authorize', [ - js.JsObject.jsify(json), - (js.JsObject jsTokenObject) { - try { - final result = _processToken(jsTokenObject, responseTypes); - completer.complete(result); - } catch (e, stack) { - html.window.console.error(jsTokenObject); - completer.completeError(e, stack); - } - } - ]); - - return completer.future; - } - - LoginResult _processToken( - js.JsObject jsTokenObject, - List? responseTypes, - ) { - final error = jsTokenObject['error']; - - if (error != null) { - final details = jsTokenObject['details'] as String?; - throw UserConsentException( - 'Failed to get user consent: $error.', - details: details, - ); - } - - final tokenType = jsTokenObject['token_type']; - final token = jsTokenObject['access_token'] as String?; - - if (token == null || tokenType != 'Bearer') { - throw AuthenticationException( - 'Failed to obtain user consent. Invalid server response.', - ); - } - - final idToken = jsTokenObject['id_token'] as String?; - - if (responseTypes?.contains(ResponseType.idToken) == true && - idToken?.isNotEmpty != true) { - throw AuthenticationException('Expected to get id_token, but did not.'); - } - - final scopeString = jsTokenObject['scope'] as String; - final scopes = scopeString.split(' '); - - final expiresAt = jsTokenObject['expires_at'] as int; - final expiresAtDate = - DateTime.fromMillisecondsSinceEpoch(expiresAt).toUtc(); - - final accessToken = AccessToken('Bearer', token, expiresAtDate); - - final credentials = AccessCredentials( - accessToken, - null, - scopes, - idToken: idToken, - ); - - String? code; - if (responseTypes?.contains(ResponseType.code) == true) { - code = jsTokenObject['code'] as String?; - - if (code == null) { - throw AuthenticationException( - 'Expected to get auth code from server in hybrid flow, but did not.', - ); - } - } - return LoginResult(credentials, code: code); - } -} - -class LoginResult { - final AccessCredentials credential; - final String? code; - - LoginResult(this.credential, {this.code}); -} - -/// Convert [responseType] to string value expected by `gapi.auth.authorize`. -String _responseTypeToString(ResponseType responseType) { - switch (responseType) { - case ResponseType.code: - return 'code'; - case ResponseType.idToken: - return 'id_token'; - case ResponseType.permission: - return 'permission'; - case ResponseType.token: - return 'token'; - } -} - -js.JsObject get _gapiAuth2 => - (js.context['gapi'] as js.JsObject)['auth2'] as js.JsObject; diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/jwt.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/jwt.dart deleted file mode 100644 index 24004f81..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/jwt.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; -import 'dart:convert'; - -import 'package:http/http.dart' as http; - -import '../../auth_io.dart'; -import '../crypto/rsa.dart'; -import '../crypto/rsa_sign.dart'; -import '../known_uris.dart'; -import '../utils.dart'; -import 'base_flow.dart'; - -/// Currently only supports the Google auth provider. -class JwtFlow extends BaseFlow { - // All details are described at: - // https://developers.google.com/accounts/docs/OAuth2ServiceAccount - // JSON Web Signature (JWS) requires signing a string with a private key. - - final String _clientEmail; - final RS256Signer _signer; - final List _scopes; - final String? _user; - final http.Client _client; - - JwtFlow( - this._clientEmail, - RSAPrivateKey key, - this._user, - this._scopes, - this._client, - ) : _signer = RS256Signer(key); - - @override - Future run() async { - final timestamp = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000 - - maxExpectedTimeDiffInSeconds; - - final jwtHeader = {'alg': 'RS256', 'typ': 'JWT'}; - final jwtHeaderBase64 = _base64url(ascii.encode(jsonEncode(jwtHeader))); - - final jwtClaimSet = { - 'iss': _clientEmail, - 'scope': _scopes.join(' '), - 'aud': googleOauth2TokenEndpoint.toString(), - 'exp': timestamp + 3600, - 'iat': timestamp, - if (_user != null) 'sub': _user!, - }; - final jwtClaimSetBase64 = _base64url(utf8.encode(jsonEncode(jwtClaimSet))); - - final jwtSignatureInput = '$jwtHeaderBase64.$jwtClaimSetBase64'; - final jwtSignatureInputInBytes = ascii.encode(jwtSignatureInput); - - final signature = _signer.sign(jwtSignatureInputInBytes); - final jwt = '$jwtSignatureInput.${_base64url(signature)}'; - - // https://developers.google.com/identity/protocols/oauth2/service-account#authorizingrequests - final response = await _client.oauthTokenRequest( - { - 'grant_type': _uri, - 'assertion': jwt, - }, - authEndpoints: GoogleAuthEndpoints(), - ); - final accessToken = parseAccessToken(response); - return AccessCredentials(accessToken, null, _scopes); - } -} - -const _uri = 'urn:ietf:params:oauth:grant-type:jwt-bearer'; - -String _base64url(List bytes) => - base64Url.encode(bytes).replaceAll('=', ''); diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/metadata_server.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/metadata_server.dart deleted file mode 100644 index 26bf224e..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/metadata_server.dart +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; -import 'dart:io'; - -import 'package:http/http.dart' as http; - -import '../access_credentials.dart'; -import '../utils.dart'; -import 'base_flow.dart'; - -/// Obtains access credentials form the metadata server. -/// -/// Using this class assumes that the current program is running a -/// ComputeEngine VM. It will retrieve the current access token from the -/// metadata server, looking first for one set in the environment under -/// `$GCE_METADATA_HOST`. -class MetadataServerAuthorizationFlow extends BaseFlow { - static const _headers = {'Metadata-Flavor': 'Google'}; - static const _serviceAccountUrlInfix = - 'computeMetadata/v1/instance/service-accounts'; - // https://cloud.google.com/compute/docs/storing-retrieving-metadata#querying - static const _defaultMetadataHost = 'metadata.google.internal'; - static const _gceMetadataHostEnvVar = 'GCE_METADATA_HOST'; - - final String email; - final Uri _scopesUrl; - final Uri _tokenUrl; - final http.Client _client; - - factory MetadataServerAuthorizationFlow( - http.Client client, { - String email = 'default', - }) { - final encodedEmail = Uri.encodeComponent(email); - - final metadataHost = - Platform.environment[_gceMetadataHostEnvVar] ?? _defaultMetadataHost; - final serviceAccountPrefix = - 'http://$metadataHost/$_serviceAccountUrlInfix'; - - final scopesUrl = Uri.parse('$serviceAccountPrefix/$encodedEmail/scopes'); - final tokenUrl = Uri.parse('$serviceAccountPrefix/$encodedEmail/token'); - return MetadataServerAuthorizationFlow._( - client, - email, - scopesUrl, - tokenUrl, - ); - } - - MetadataServerAuthorizationFlow._( - this._client, - this.email, - this._scopesUrl, - this._tokenUrl, - ); - - @override - Future run() async { - final results = await Future.wait( - [ - _client.requestJson( - http.Request('GET', _tokenUrl)..headers.addAll(_headers), - 'Failed to obtain access credentials.', - ), - _getScopes() - ], - ); - final json = results.first as Map; - final accessToken = parseAccessToken(json); - - final scopes = (results.last as String) - .replaceAll('\n', ' ') - .split(' ') - .where((part) => part.isNotEmpty) - .toList(); - - return AccessCredentials( - accessToken, - null, - scopes, - ); - } - - Future _getScopes() async { - final response = await _client.get(_scopesUrl, headers: _headers); - return response.body; - } -} diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/token_model.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/token_model.dart deleted file mode 100644 index 0fb2b411..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/token_model.dart +++ /dev/null @@ -1,178 +0,0 @@ -import 'dart:async'; -import 'dart:html'; -import 'dart:js'; - -import '../access_credentials.dart'; -import '../access_token.dart'; -import '../authentication_exception.dart'; -import '../browser_utils.dart'; -import '../utils.dart'; -import 'token_model_interop.dart' as interop; - -JsObject get _googleAccountsId => - ((context['google'] as JsObject)['accounts'] as JsObject)['id'] as JsObject; - -/// Obtains [AccessCredentials] using the -/// [Google Identity Services](https://developers.google.com/identity/oauth2/web/guides/overview) -/// token model. -/// -/// The returned [AccessCredentials] will *always* have a `null` value for -/// [AccessCredentials.refreshToken] and -/// [AccessCredentials.idToken]. -/// -/// See -/// [Choose a user authorization model](https://developers.google.com/identity/oauth2/web/guides/choose-authorization-model) -/// to understand the tradeoffs between using this function and -/// [requestAuthorizationCode]. -/// -/// See https://developers.google.com/identity/oauth2/web/guides/use-token-model -/// and https://developers.google.com/identity/oauth2/web/reference/js-reference -/// for more details. -Future requestAccessCredentials({ - required String clientId, - required Iterable scopes, - String prompt = 'select_account', - @Deprecated('Undocumented feature. Do not include in production code.') - String? logLevel, -}) async { - await initializeScript('https://accounts.google.com/gsi/client'); - if (logLevel != null) _googleAccountsId.callMethod('setLogLevel', [logLevel]); - - final completer = Completer(); - - void callback(interop.TokenResponse response) { - if (response.error != null) { - window.console.log(response); - completer.completeError( - AuthenticationException( - response.error!, - errorDescription: response.error_description, - errorUri: response.error_uri, - ), - ); - return; - } - - final token = AccessToken( - response.token_type, - response.access_token, - expiryDate(response.expires_in), - ); - - final creds = AccessCredentials(token, null, response.scope.split(' ')); - - completer.complete(creds); - } - - final config = interop.TokenClientConfig( - callback: allowInterop(callback), - client_id: clientId, - scope: scopes.toSet().join(' '), - prompt: prompt, - ); - - final client = interop.initTokenClient(config); - - client.requestAccessToken(); - - return completer.future; -} - -/// Obtains [CodeResponse] using the -/// [Google Identity Services](https://developers.google.com/identity/oauth2/web/guides/overview) -/// code model. -/// -/// See -/// [Choose a user authorization model](https://developers.google.com/identity/oauth2/web/guides/choose-authorization-model) -/// to understand the tradeoffs between using this function and -/// [requestAccessCredentials]. -/// -/// See https://developers.google.com/identity/oauth2/web/guides/use-code-model -/// and https://developers.google.com/identity/oauth2/web/reference/js-reference -/// for more details. -Future requestAuthorizationCode({ - required String clientId, - required Iterable scopes, - String? state, - String? hint, - String? hostedDomain, - @Deprecated('Undocumented feature. Do not include in production code.') - String? logLevel, -}) async { - await initializeScript('https://accounts.google.com/gsi/client'); - if (logLevel != null) _googleAccountsId.callMethod('setLogLevel', [logLevel]); - - final completer = Completer(); - - void callback(interop.CodeResponse response) { - if (response.error != null) { - window.console.log(response); - completer.completeError( - AuthenticationException( - response.error!, - errorDescription: response.error_description, - errorUri: response.error_uri, - ), - ); - return; - } - - completer.complete(CodeResponse._( - code: response.code, - scopes: response.scope.split(' '), - state: response.state, - )); - } - - final config = interop.CodeClientConfig( - callback: allowInterop(callback), - client_id: clientId, - scope: scopes.toSet().join(' '), - state: state, - hint: hint, - hosted_domain: hostedDomain, - ); - - final client = interop.initCodeClient(config); - - client.requestCode(); - - return completer.future; -} - -/// Revokes all of the scopes that the user granted to the app. -/// -/// A valid [accessTokenValue] is required to revoke the permission. -Future revokeConsent(String accessTokenValue) { - final completer = Completer(); - - void done(Object? arg) { - window.console.log(arg); - completer.complete(); - } - - interop.revoke(accessTokenValue, allowInterop(done)); - - return completer.future; -} - -/// Result from a successful call to [requestAuthorizationCode]. -/// -/// See https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeResponse -/// for more details. -class CodeResponse { - CodeResponse._({required this.code, required this.scopes, this.state}); - - /// The authorization code of a successful token response. - final String code; - - /// The list of scopes that are approved by the user. - final List scopes; - - /// The string value that your application uses to maintain state between your - /// authorization request and the response. - final String? state; - - @override - String toString() => 'CodeResponse: $code'; -} diff --git a/third_party/googleapis_auth/lib/src/oauth2_flows/token_model_interop.dart b/third_party/googleapis_auth/lib/src/oauth2_flows/token_model_interop.dart deleted file mode 100644 index 7a1a8fe3..00000000 --- a/third_party/googleapis_auth/lib/src/oauth2_flows/token_model_interop.dart +++ /dev/null @@ -1,115 +0,0 @@ -// ignore_for_file: non_constant_identifier_names - -@JS('google.accounts.oauth2') -library token_model_interop; - -import 'package:js/js.dart'; - -@JS() -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#google.accounts.oauth2.initTokenClient -external TokenClient initTokenClient(TokenClientConfig config); - -@JS() -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#google.accounts.oauth2.revoke -external void revoke(String accessToken, [void Function(Object?) done]); - -@JS() -@anonymous -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#TokenClientConfig -class TokenClientConfig { - external factory TokenClientConfig({ - required String client_id, - required String scope, - required void Function(TokenResponse) callback, - String hint, - String hosted_domain, - String prompt, - }); - - // state: not recommended - // enable_serial_consent: skipping. only for old clients -} - -@JS() -@anonymous -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#TokenResponse -class TokenResponse { - external String get access_token; - external int get expires_in; - external String get hd; - external String get prompt; - external String get token_type; - external String get scope; - - /// A single ASCII error code. - external String? get error; - - /// Human-readable ASCII text providing additional information, used to assist - /// the client developer in understanding the error that occurred. - external String? get error_description; - - /// A URI identifying a human-readable web page with information about the - /// error, used to provide the client developer with additional information - /// about the error. - external String? get error_uri; -} - -@JS() -@anonymous -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#google.accounts.oauth2.initTokenClient -class TokenClient { - external void requestAccessToken(); -} - -@JS() -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#google.accounts.oauth2.initCodeClient -external CodeClient initCodeClient(CodeClientConfig config); - -@JS() -@anonymous -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#CodeClient -class CodeClient { - external void requestCode(); -} - -@JS() -@anonymous -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#CodeClientConfig -class CodeClientConfig { - external factory CodeClientConfig({ - required String client_id, - required String scope, - String? redirect_uri, - required void Function(CodeResponse) callback, - String? state, - String? hint, - String? hosted_domain, - String? ux_mode, - bool select_account, - }); -} - -@JS() -@anonymous -// https://developers.google.com/identity/oauth2/web/reference/js-reference?hl=en#CodeResponse -class CodeResponse { - external String get code; - external String get scope; - external String? get state; - - external String get authuser; - external String get hd; - external String get prompt; - - /// A single ASCII error code. - external String? get error; - - /// Human-readable ASCII text providing additional information, used to assist - /// the client developer in understanding the error that occurred. - external String? get error_description; - - /// A URI identifying a human-readable web page with information about the - /// error, used to provide the client developer with additional information - /// about the error. - external String? get error_uri; -} diff --git a/third_party/googleapis_auth/lib/src/response_type.dart b/third_party/googleapis_auth/lib/src/response_type.dart deleted file mode 100644 index daebe33a..00000000 --- a/third_party/googleapis_auth/lib/src/response_type.dart +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2021, the Dart 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. - -/// Available response types that can be requested when using the implicit -/// browser login flow. -/// -/// More information about these values can be found here: -/// https://developers.google.com/identity/protocols/oauth2/openid-connect#response-type -enum ResponseType { - /// Requests an access code. This triggers the basic rather than the implicit - /// flow. - code, - - /// Requests the user's identity token when running the implicit flow. - idToken, - - /// Requests the user's current permissions. - permission, - - /// Requests the user's access token when running the implicit flow. - token, -} diff --git a/third_party/googleapis_auth/lib/src/service_account_client.dart b/third_party/googleapis_auth/lib/src/service_account_client.dart deleted file mode 100644 index ecc5e632..00000000 --- a/third_party/googleapis_auth/lib/src/service_account_client.dart +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2021, the Dart 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 'package:http/http.dart'; - -import 'oauth2_flows/base_flow.dart'; -import 'oauth2_flows/jwt.dart'; -import 'service_account_credentials.dart'; - -/// Obtain oauth2 [AccessCredentials] using service account credentials. -/// -/// In case the service account has no access to the requested scopes or another -/// error occurs the returned future will complete with an `Exception`. -/// -/// {@macro googleapis_auth_client_for_creds} -/// -/// The [ServiceAccountCredentials] can be obtained in the Google Cloud Console. -Future obtainAccessCredentialsViaServiceAccount( - ServiceAccountCredentials clientCredentials, - List scopes, - Client client, -) => - JwtFlow( - clientCredentials.email, - clientCredentials.privateRSAKey, - clientCredentials.impersonatedUser, - scopes, - client, - ).run(); - -/// Obtains oauth2 credentials and returns an authenticated HTTP client. -/// -/// See [obtainAccessCredentialsViaServiceAccount] for specifics about the -/// arguments used for obtaining access credentials. -/// -/// {@macro googleapis_auth_returned_auto_refresh_client} -/// -/// {@macro googleapis_auth_baseClient_param} -/// -/// {@macro googleapis_auth_close_the_client} -/// {@macro googleapis_auth_not_close_the_baseClient} -Future clientViaServiceAccount( - ServiceAccountCredentials clientCredentials, - List scopes, { - Client? baseClient, -}) async => - await clientFromFlow( - (c) => JwtFlow( - clientCredentials.email, - clientCredentials.privateRSAKey, - clientCredentials.impersonatedUser, - scopes, - c, - ), - baseClient: baseClient, - ); diff --git a/third_party/googleapis_auth/lib/src/service_account_credentials.dart b/third_party/googleapis_auth/lib/src/service_account_credentials.dart deleted file mode 100644 index 9c02e22b..00000000 --- a/third_party/googleapis_auth/lib/src/service_account_credentials.dart +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2014, the Dart 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:convert'; - -import 'client_id.dart'; -import 'crypto/pem.dart'; -import 'crypto/rsa.dart'; - -export 'access_credentials.dart' show AccessCredentials; -export 'access_token.dart' show AccessToken; -export 'auth_client.dart'; -export 'client_id.dart'; -export 'exceptions.dart'; -export 'response_type.dart'; - -/// Represents credentials for a service account. -class ServiceAccountCredentials { - /// The email address of this service account. - final String email; - - /// The clientId. - final ClientId clientId; - - /// Private key. - final String privateKey; - - /// Impersonated user, if any. If not impersonating any user this is `null`. - final String? impersonatedUser; - - /// Private key as an [RSAPrivateKey]. - final RSAPrivateKey privateRSAKey; - - /// Creates a new [ServiceAccountCredentials] from JSON. - /// - /// [json] can be either a [Map] or a JSON map encoded as a [String]. - /// - /// The optional named argument [impersonatedUser] is used to set the user - /// to impersonate if impersonating a user. - factory ServiceAccountCredentials.fromJson(Object? json, - {String? impersonatedUser}) { - if (json is String) { - json = jsonDecode(json); - } - if (json is! Map) { - throw ArgumentError('json must be a Map or a String encoding a Map.'); - } - final identifier = json['client_id'] as String?; - final privateKey = json['private_key'] as String?; - final email = json['client_email'] as String?; - final type = json['type']; - - if (type != 'service_account') { - throw ArgumentError( - 'The given credentials are not of type ' - 'service_account (was: $type).', - ); - } - - if (identifier == null || privateKey == null || email == null) { - throw ArgumentError( - 'The given credentials do not contain all the ' - 'fields: client_id, private_key and client_email.', - ); - } - - final clientId = ClientId(identifier); - return ServiceAccountCredentials( - email, - clientId, - privateKey, - impersonatedUser: impersonatedUser, - ); - } - - /// Creates a new [ServiceAccountCredentials]. - /// - /// [email] is the e-mail address of the service account. - /// - /// [clientId] is the client ID for the service account. - /// - /// [privateKey] is the base 64 encoded, unencrypted private key, including - /// the '-----BEGIN PRIVATE KEY-----' and '-----END PRIVATE KEY-----' - /// boundaries. - /// - /// The optional named argument [impersonatedUser] is used to set the user - /// to impersonate if impersonating a user is needed. - ServiceAccountCredentials( - this.email, - this.clientId, - this.privateKey, { - this.impersonatedUser, - }) : privateRSAKey = keyFromString(privateKey); -} diff --git a/third_party/googleapis_auth/lib/src/typedefs.dart b/third_party/googleapis_auth/lib/src/typedefs.dart deleted file mode 100644 index 3228e116..00000000 --- a/third_party/googleapis_auth/lib/src/typedefs.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -/// Function for directing the user or it's user-agent to [uri]. -/// -/// The user is required to go to [uri] and either approve or decline the -/// application's request for access resources on their behalf. -typedef PromptUserForConsent = void Function(String uri); - -/// Function for directing the user or it's user-agent to [uri]. -/// -/// The user is required to go to [uri] and either approve or decline the -/// application's request for access resources on their behalf. -/// -/// The user will be given an authorization code. This function should complete -/// with this authorization code. If the user declined to give access this -/// function should complete with an error. -typedef PromptUserForConsentManual = Future Function(String uri); diff --git a/third_party/googleapis_auth/lib/src/utils.dart b/third_party/googleapis_auth/lib/src/utils.dart deleted file mode 100644 index 41e9776a..00000000 --- a/third_party/googleapis_auth/lib/src/utils.dart +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2014, the Dart 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:convert'; - -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart' show BaseRequest, Client, StreamedResponse; -import 'package:http_parser/http_parser.dart'; - -import 'http_client_base.dart'; - -/// Due to differences of clock speed, network latency, etc. we -/// will shorten expiry dates by 20 seconds. -const maxExpectedTimeDiffInSeconds = 20; - -AccessToken parseAccessToken(Map jsonMap) { - final tokenType = jsonMap['token_type']; - final accessToken = jsonMap['access_token']; - final expiresIn = jsonMap['expires_in']; - - if (accessToken is! String || expiresIn is! int || tokenType != 'Bearer') { - throw ServerRequestFailedException( - 'Failed to exchange authorization code. Invalid server response.', - responseContent: jsonMap, - ); - } - - return AccessToken('Bearer', accessToken, expiryDate(expiresIn)); -} - -/// Constructs a [DateTime] which is [seconds] seconds from now with -/// an offset of [maxExpectedTimeDiffInSeconds]. Result is UTC time. -DateTime expiryDate(int seconds) => DateTime.now() - .toUtc() - .add(Duration(seconds: seconds - maxExpectedTimeDiffInSeconds)); - -/// Constant for the 'application/x-www-form-urlencoded' content type -const _contentTypeUrlEncoded = - 'application/x-www-form-urlencoded; charset=utf-8'; - -Future> _readJsonMapFromResponse( - StreamedResponse response, -) async { - await _expectJsonResponse(response); - - Object? jsonValue; - - final bytes = await response.stream.toBytes(); - - late String string; - try { - string = utf8.decode(bytes); - } on FormatException catch (e) { - throw ServerRequestFailedException( - 'The response was not valid UTF-8. ' - '$e', - statusCode: response.statusCode, - responseContent: bytes, - ); - } - - try { - jsonValue = jsonDecode(string); - } on FormatException catch (e) { - throw ServerRequestFailedException( - 'Could not decode the response as JSON. ' - '$e', - statusCode: response.statusCode, - responseContent: string, - ); - } - - if (jsonValue is! Map) { - throw ServerRequestFailedException( - 'The returned JSON response was not a Map.', - statusCode: response.statusCode, - responseContent: jsonValue, - ); - } - - return jsonValue; -} - -extension ClientExtensions on Client { - Future> requestJson( - BaseRequest request, - String errorHeader, - ) async { - final response = await send(request); - final jsonMap = await _readJsonMapFromResponse(response); - - if (response.statusCode != 200) { - final error = _errorStringFromJsonResponse(jsonMap); - final message = [ - errorHeader, - if (error != null) error, - ].join(' '); - throw ServerRequestFailedException( - message, - statusCode: response.statusCode, - responseContent: jsonMap, - ); - } - - return jsonMap; - } - - Future> oauthTokenRequest( - Map postValues, { - required AuthEndpoints authEndpoints, - }) async { - final body = Stream>.value( - ascii.encode( - postValues.entries - .map((e) => '${e.key}=${Uri.encodeComponent(e.value)}') - .join('&'), - ), - ); - final request = RequestImpl('POST', authEndpoints.tokenEndpoint, body) - ..headers['content-type'] = _contentTypeUrlEncoded; - - return requestJson(request, 'Failed to obtain access credentials.'); - } -} - -/// Returns an error string for [json] if it contains error data in keys -/// `error` and `error_description`. -/// -/// Otherwise, returns `null`. -String? _errorStringFromJsonResponse(Map json) { - final error = json['error']; - final values = [ - if (error != null) 'Error: $error', - json['error_description'], - ].where((element) => element != null).join(' '); - if (values.isEmpty) return null; - return values; -} - -Future _expectJsonResponse(StreamedResponse response) async { - final contentType = response.headers['content-type']; - - if (!_isJson(contentType)) { - String? body; - try { - body = await response.stream.bytesToString(); - } catch (_) { - /// We're already going to throw below - } - - final message = contentType == null - ? 'Server responded without a content type header.' - : 'Server responded with invalid content type: $contentType. '; - - throw ServerRequestFailedException( - '$message Expected a JSON response.', - statusCode: response.statusCode, - responseContent: body, - ); - } -} - -/// Follows https://mimesniff.spec.whatwg.org/#json-mime-type -bool _isJson(String? contentType) { - if (contentType == null) return false; - final mediaType = MediaType.parse(contentType); - if (mediaType.mimeType == 'application/json') return true; - if (mediaType.mimeType == 'text/json') return true; - return mediaType.subtype.endsWith('+json'); -} diff --git a/third_party/googleapis_auth/mono_pkg.yaml b/third_party/googleapis_auth/mono_pkg.yaml deleted file mode 100644 index 444b6208..00000000 --- a/third_party/googleapis_auth/mono_pkg.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# See https://pub.dev/packages/mono_repo -sdk: -- pubspec -- dev - -stages: -- analyze_and_format: - - group: - - format - - analyze: --fatal-infos . - sdk: dev - - group: - - analyze - sdk: pubspec -- unittest: - - test: -p vm - - test: -p chrome diff --git a/third_party/googleapis_auth/pubspec.yaml b/third_party/googleapis_auth/pubspec.yaml deleted file mode 100644 index db58cbfe..00000000 --- a/third_party/googleapis_auth/pubspec.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: googleapis_auth -version: 1.4.1 -description: Obtain Access credentials for Google services using OAuth 2.0 -repository: https://github.com/google/googleapis.dart/tree/master/googleapis_auth - -environment: - sdk: '>=2.19.0 <3.0.0' - -dependencies: - args: ^2.3.1 - crypto: ^3.0.0 - http: '>=0.13.5 <2.0.0' - http_parser: ^4.0.0 - js: ^0.6.4 - -dev_dependencies: - # build_ dependencies allow debugging web tests using: - # `dart pub run build_runner serve test` - build_runner: ^2.0.0 - build_test: ^2.0.0 - build_web_compilers: '>=3.2.7 <5.0.0' - dart_flutter_team_lints: ^1.0.0 - test: ^1.16.0 - -false_secrets: -- test/test_utils.dart diff --git a/third_party/googleapis_auth/test/adc_test.dart b/third_party/googleapis_auth/test/adc_test.dart deleted file mode 100644 index 8063b981..00000000 --- a/third_party/googleapis_auth/test/adc_test.dart +++ /dev/null @@ -1,131 +0,0 @@ -@TestOn('vm') -library googleapis_auth.adc_test; - -import 'dart:convert'; -import 'dart:io'; - -import 'package:googleapis_auth/auth_io.dart'; -import 'package:googleapis_auth/src/adc_utils.dart' - show fromApplicationsCredentialsFile; -import 'package:googleapis_auth/src/known_uris.dart'; -import 'package:http/http.dart'; -import 'package:test/test.dart'; - -import 'test_utils.dart'; - -void main() { - test('fromApplicationsCredentialsFile', () async { - final authEndpoints = GoogleAuthEndpoints(); - final tmp = await Directory.systemTemp.createTemp('googleapis_auth-test'); - try { - final credsFile = File.fromUri(tmp.uri.resolve('creds.json')); - await credsFile.writeAsString(json.encode({ - 'client_id': 'id', - 'client_secret': 'secret', - 'refresh_token': 'refresh', - 'type': 'authorized_user' - })); - final c = await fromApplicationsCredentialsFile( - credsFile, - authEndpoints, - 'test-credentials-file', - [], - mockClient((Request request) async { - final url = request.url; - if (url == googleOauth2TokenEndpoint) { - expect(request.method, equals('POST')); - expect( - request.body, - equals('client_id=id&' - 'client_secret=secret&' - 'refresh_token=refresh&' - 'grant_type=refresh_token')); - final body = jsonEncode({ - 'token_type': 'Bearer', - 'access_token': 'atoken', - 'expires_in': 3600, - }); - return Response(body, 200, headers: jsonContentType); - } - if (url.toString() == - 'https://storage.googleapis.com/b/bucket/o/obj') { - expect(request.method, equals('GET')); - expect(request.headers['Authorization'], equals('Bearer atoken')); - expect(request.headers['X-Goog-User-Project'], isNull); - return Response('hello world', 200); - } - return Response('bad', 404); - }), - ); - expect(c.credentials.accessToken.data, equals('atoken')); - - final r = - await c.get(Uri.https('storage.googleapis.com', '/b/bucket/o/obj')); - expect(r.statusCode, equals(200)); - expect(r.body, equals('hello world')); - - c.close(); - } finally { - await tmp.delete(recursive: true); - } - }); - - test('fromApplicationsCredentialsFile w. quota_project_id', () async { - final tmp = await Directory.systemTemp.createTemp('googleapis_auth-test'); - try { - final credsFile = File.fromUri(tmp.uri.resolve('creds.json')); - await credsFile.writeAsString(json.encode({ - 'client_id': 'id', - 'client_secret': 'secret', - 'refresh_token': 'refresh', - 'type': 'authorized_user', - 'quota_project_id': 'project' - })); - final c = await fromApplicationsCredentialsFile( - credsFile, - GoogleAuthEndpoints(), - 'test-credentials-file', - [], - mockClient((Request request) async { - final url = request.url; - if (url == googleOauth2TokenEndpoint) { - expect(request.method, equals('POST')); - expect( - request.body, - equals( - 'client_id=id&' - 'client_secret=secret&' - 'refresh_token=refresh&' - 'grant_type=refresh_token', - ), - ); - final body = jsonEncode({ - 'token_type': 'Bearer', - 'access_token': 'atoken', - 'expires_in': 3600, - }); - return Response(body, 200, headers: jsonContentType); - } - if (url.toString() == - 'https://storage.googleapis.com/b/bucket/o/obj') { - expect(request.method, equals('GET')); - expect(request.headers['Authorization'], equals('Bearer atoken')); - expect(request.headers['X-Goog-User-Project'], equals('project')); - return Response('hello world', 200); - } - return Response('bad', 404); - }), - ); - expect(c.credentials.accessToken.data, equals('atoken')); - - final r = - await c.get(Uri.https('storage.googleapis.com', '/b/bucket/o/obj')); - expect(r.statusCode, equals(200)); - expect(r.body, equals('hello world')); - - c.close(); - } finally { - await tmp.delete(recursive: true); - } - }); -} diff --git a/third_party/googleapis_auth/test/crypto/asn1_test.dart b/third_party/googleapis_auth/test/crypto/asn1_test.dart deleted file mode 100644 index 8311cfc4..00000000 --- a/third_party/googleapis_auth/test/crypto/asn1_test.dart +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2014, the Dart 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:typed_data'; - -import 'package:googleapis_auth/src/crypto/asn1.dart'; -import 'package:test/test.dart'; - -void main() { - void expectArgumentError(List bytes) { - expect(() => ASN1Parser.parse(Uint8List.fromList(bytes)), - throwsA(isArgumentError)); - } - - void invalidLenTest(int tagBytes) { - test('invalid-len', () { - expectArgumentError([tagBytes]); - expectArgumentError([tagBytes, 0x07]); - expectArgumentError([tagBytes, 0x82]); - expectArgumentError([tagBytes, 0x82, 1]); - expectArgumentError([tagBytes, 0x01, 1, 2, 3, 4]); - }); - } - - group('asn1-parser', () { - group('sequence', () { - test('empty', () { - final sequenceBytes = [ASN1Parser.sequenceTag, 0]; - final sequence = ASN1Parser.parse(Uint8List.fromList(sequenceBytes)); - expect(sequence is ASN1Sequence, isTrue); - expect((sequence as ASN1Sequence).objects, isEmpty); - }); - - test('one-element', () { - final sequenceBytes = [ - ASN1Parser.sequenceTag, - 1, - ASN1Parser.nullTag, - 0 - ]; - final sequence = ASN1Parser.parse(Uint8List.fromList(sequenceBytes)); - expect(sequence is ASN1Sequence, isTrue); - expect((sequence as ASN1Sequence).objects, hasLength(1)); - expect(sequence.objects[0] is ASN1Null, isTrue); - }); - - test('many-elements', () { - final sequenceBytes = [ASN1Parser.sequenceTag, 0x82, 0x01, 0x00]; - for (var i = 0; i < 128; i++) { - sequenceBytes.addAll([ASN1Parser.nullTag, 0]); - } - - final sequence = ASN1Parser.parse(Uint8List.fromList(sequenceBytes)); - expect(sequence is ASN1Sequence, isTrue); - expect((sequence as ASN1Sequence).objects.length, equals(128)); - for (var i = 0; i < 128; i++) { - expect(sequence.objects[i] is ASN1Null, isTrue); - } - }); - - invalidLenTest(ASN1Parser.sequenceTag); - }); - - group('integer', () { - test('small', () { - for (var i = 0; i < 256; i++) { - final integerBytes = [ASN1Parser.integerTag, 1, i]; - final integer = - ASN1Parser.parse(Uint8List.fromList(integerBytes)) as ASN1Integer; - expect(integer.integer, BigInt.from(i)); - } - }); - - test('multi-byte', () { - final integerBytes = [ASN1Parser.integerTag, 3, 1, 2, 3]; - final integer = ASN1Parser.parse(Uint8List.fromList(integerBytes)); - expect(integer is ASN1Integer, isTrue); - expect((integer as ASN1Integer).integer, BigInt.from(0x010203)); - }); - - invalidLenTest(ASN1Parser.integerTag); - }); - - group('octet-string', () { - test('small', () { - final octetStringBytes = [ASN1Parser.octetStringTag, 3, 1, 2, 3]; - final octetString = - ASN1Parser.parse(Uint8List.fromList(octetStringBytes)); - expect(octetString is ASN1OctetString, isTrue); - expect((octetString as ASN1OctetString).bytes, equals([1, 2, 3])); - }); - - test('large', () { - final octetStringBytes = [ASN1Parser.octetStringTag, 0x82, 0x01, 0x00]; - for (var i = 0; i < 256; i++) { - octetStringBytes.add(i % 256); - } - - final octetString = - ASN1Parser.parse(Uint8List.fromList(octetStringBytes)); - expect(octetString is ASN1OctetString, isTrue); - final castedOctetString = octetString as ASN1OctetString; - for (var i = 0; i < 256; i++) { - expect(castedOctetString.bytes[i], equals(i % 256)); - } - }); - - invalidLenTest(ASN1Parser.octetStringTag); - }); - - group('oid', () { - // NOTE: Currently the oid is parsed as normal bytes, so we don't validate - // the oid structure. - test('small', () { - final objIdBytes = [ASN1Parser.objectIdTag, 3, 1, 2, 3]; - final objId = ASN1Parser.parse(Uint8List.fromList(objIdBytes)); - expect(objId is ASN1ObjectIdentifier, isTrue); - expect((objId as ASN1ObjectIdentifier).bytes, equals([1, 2, 3])); - }); - - test('large', () { - final objIdBytes = [ASN1Parser.objectIdTag, 0x82, 0x01, 0x00]; - for (var i = 0; i < 256; i++) { - objIdBytes.add(i % 256); - } - - final objId = ASN1Parser.parse(Uint8List.fromList(objIdBytes)); - expect(objId is ASN1ObjectIdentifier, isTrue); - final castedObjId = objId as ASN1ObjectIdentifier; - for (var i = 0; i < 256; i++) { - expect(castedObjId.bytes[i], equals(i % 256)); - } - }); - - invalidLenTest(ASN1Parser.objectIdTag); - }); - }); - - test('null', () { - final objId = - ASN1Parser.parse(Uint8List.fromList([ASN1Parser.nullTag, 0x00])); - expect(objId is ASN1Null, isTrue); - - expectArgumentError([ASN1Parser.nullTag]); - expectArgumentError([ASN1Parser.nullTag, 0x01]); - }); -} diff --git a/third_party/googleapis_auth/test/crypto/pem_test.dart b/third_party/googleapis_auth/test/crypto/pem_test.dart deleted file mode 100644 index 59ddc17c..00000000 --- a/third_party/googleapis_auth/test/crypto/pem_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -// ignore_for_file: lines_longer_than_80_chars - -import 'package:googleapis_auth/src/crypto/pem.dart'; -import 'package:test/test.dart'; - -import '../test_utils.dart'; - -void main() { - group('pem', () { - test('null', () { - expect(() => keyFromString(''), throwsA(isArgumentError)); - }); - - test('pem--key-from-string', () { - final key = keyFromString(testPrivateKeyString); - expect( - key.p, - equals(BigInt.parse( - '170185878019789847607218833833962851295383479739128068911675681859184825725303329240997154492057125840628991571181411414164882361723231273391547091096391845233984484218520948165420605211532206383859989286454330226302062891556391372178426684136261758077913279309249468965000813860343415338472623037185763380093'))); - expect( - key.q, - equals(BigInt.parse( - '136634937867625346722869734066327766542560453705266659651284573193680854438532412351608161985232086174999341126075829838477923122149398705411098928405144549034231120055200290950893136823181693383585861140730929930114638604738429489364496581584222788741142343940831827356789459450282075298628271623617861448279'))); - expect( - key.n, - equals(BigInt.parse( - '23253336869181252005308127869627478511861722018560725538542603352356752658510633204810959681459083455055115233727694253121121138828979138624495569601457246561359553177524606534054439784597124760679930421448728375265700767584567585959695707287695356045087640902894887625471020788794811661755081070077086649519865067918501869783817745592796089436450623267438942174934673417424553992577792939276705103879103955476795626469391055763713456179432199172562526422301070938382514265029982800538033050279129668807032677927531973249309321914500317007151921938466293582589451642241740444272968677617027011566610435323463337709947'))); - expect( - key.d, - equals(BigInt.parse( - '21186554940454261253047269959735660724480631477978821785517431853394668885438560354085051566279884512080977781045029208574826211785037495240030508751426142586201712610225510861978099522679761260199887167944008250970681053969661407950094604171122649803382413195502685962008111346880629170494825836648656453852203519401121722270587408277317819537925146228717860401265662699719826243356610955461998054615517371279631680512102389979478015385709644867750888484550190071229275090881149432467365050794063725847869274512118390103343213000471284707060203072264487986083004823016463235156640750689592865369834958756866148520449'))); - expect(key.e, equals(BigInt.from(65537))); - expect( - key.dmp1, - equals(BigInt.parse( - '8112374428701702609593842209702915108210293280208677346843383586799722226617751812699316578927727255231777006398991855865405686833748485558923861522271817820635175987589597358267451526325993144989526626651865780047418167954318425419006133348210655541684866328365584952723843668457708310075048817739114161457'))); - expect( - key.dmq1, - equals(BigInt.parse( - '69064888333930830841944331910451194321610695483381427808232052980561601308959263072336597373770287299070802348040252301131546443496698520136006747353055884093824470361301555431744464097251017848208627523520965497274938325818544542688522182250340240209771921627903870254182590341478772425006618460954711021211'))); - expect( - key.coeff, - equals(BigInt.parse( - '16726959063327324857338379758571748557044292252371297447561270320393087678399207080059961434627453370656491757664831584315003981946034135341817305303511530360890203726058358401094205679273808207987503167082629712433452873772120961093571912870024590300080209978748890272607981079166485164486378666155431958545'))); - }); - }); -} diff --git a/third_party/googleapis_auth/test/crypto/rsa_sign_test.dart b/third_party/googleapis_auth/test/crypto/rsa_sign_test.dart deleted file mode 100644 index b3711fa5..00000000 --- a/third_party/googleapis_auth/test/crypto/rsa_sign_test.dart +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2014, the Dart 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:convert'; - -import 'package:googleapis_auth/src/crypto/rsa_sign.dart'; -import 'package:test/test.dart'; - -import '../test_utils.dart'; - -void main() { - group('rsa-sha256-signer', () { - final signer = RS256Signer(testPrivateKey); - - // NOTE: - // The signatures can be regenerated via the openssl commandline utility: - // $ cat plaintext | openssl dgst -sha256 -sign key.pem > ciphertext - // e.g. - // $ echo -n "hello world"|openssl dgst -sha256 -sign key.pem|hexdump -v -C - // 00000000 59 9d 6f 81 c1 0f d6 f1 58 46 2d 4d c9 b8 69 1d - // 00000010 b1 e0 e0 26 a4 de 49 d8 4f 5a ac db 81 ab 10 27 - // 00000020 3a f4 5a f8 bb da a9 84 be c7 5a fb b9 2e 0a 66 - // 00000030 8f 78 d5 cb c9 82 0b 57 36 fc bc 42 1b f5 fa 76 - // 00000040 b7 01 4c bc 2d b9 fe 20 55 62 f5 87 8c bc e3 58 - // 00000050 a6 c6 8a ef 16 c8 4a 85 01 6e df 05 43 c8 ef 35 - // 00000060 37 9f 1b 29 57 eb c7 93 89 75 f5 65 81 0a 6c 8c - // 00000070 44 35 ad 73 89 90 53 42 26 f3 31 a9 06 f1 32 20 - // 00000080 48 a3 e1 68 3d 86 67 45 74 19 91 75 c9 28 ca 8b - // 00000090 33 63 ed a2 b1 90 e6 e1 0a 1f 87 ec 02 f8 92 03 - // 000000a0 cf 0e 30 49 b0 f1 72 29 a3 9c 2e cc 7c 87 65 11 - // 000000b0 1f 38 34 d3 3e fe af 8e 31 f0 10 1f f5 71 dd 90 - // 000000c0 f6 c7 ba 5d 10 0c 63 eb a4 3c a5 17 9a 99 52 2d - // 000000d0 b6 27 96 8c e2 44 63 35 1f 04 6f b8 31 e6 d4 47 - // 000000e0 31 0d 3c 36 6c bf 14 df dc 2d 53 c7 ca d1 ec 6d - // 000000f0 95 37 2f 86 14 da 6c 04 a1 fd 45 fa 95 e0 04 bf - test('encrypt-hello-world', () { - expect( - signer.sign(ascii.encode('hello world')), - equals([ - 89, 157, 111, 129, 193, 15, 214, 241, 88, 70, 45, 77, 201, 184, //!! - 105, 29, 177, 224, 224, 38, 164, 222, 73, 216, 79, 90, 172, 219, - 129, 171, 16, 39, 58, 244, 90, 248, 187, 218, 169, 132, 190, 199, - 90, 251, 185, 46, 10, 102, 143, 120, 213, 203, 201, 130, 11, 87, 54, - 252, 188, 66, 27, 245, 250, 118, 183, 1, 76, 188, 45, 185, 254, 32, - 85, 98, 245, 135, 140, 188, 227, 88, 166, 198, 138, 239, 22, - 200, 74, 133, 1, 110, 223, 5, 67, 200, 239, 53, 55, 159, 27, 41, 87, - 235, 199, 147, 137, 117, 245, 101, 129, 10, 108, 140, 68, 53, 173, - 115, 137, 144, 83, 66, 38, 243, 49, 169, 6, 241, 50, 32, 72, 163, - 225, 104, 61, 134, 103, 69, 116, 25, 145, 117, 201, 40, 202, 139, - 51, 99, 237, 162, 177, 144, 230, 225, 10, 31, 135, 236, 2, 248, 146, - 3, 207, 14, 48, 73, 176, 241, 114, 41, 163, 156, 46, 204, 124, 135, - 101, 17, 31, 56, 52, 211, 62, 254, 175, 142, 49, 240, 16, 31, 245, - 113, 221, 144, 246, 199, 186, 93, 16, 12, 99, 235, 164, 60, 165, 23, - 154, 153, 82, 45, 182, 39, 150, 140, 226, 68, 99, 53, 31, 4, 111, - 184, 49, 230, 212, 71, 49, 13, 60, 54, 108, 191, 20, 223, 220, 45, - 83, 199, 202, 209, 236, 109, 149, 55, 47, 134, 20, 218, 108, 4, 161, - 253, 69, 250, 149, 224, 4, 191 - ])); - }); - - // $ echo -n ""|openssl dgst -sha256 -sign key.pem|hexdump -v -C - test('null-bytes', () { - expect( - signer.sign([]), - equals([ - 113, 99, 2, 245, 156, 215, 253, 172, 157, 46, 126, 165, 174, //!! - 158, 186, 213, 211, 85, 118, 63, 208, 122, 196, 214, 154, 221, 92, - 105, 27, 29, 153, 35, 91, 111, 5, 10, 82, 213, 179, 41, 165, 122, - 227, 145, 217, 108, 249, 153, 116, 80, 140, 238, 158, 140, 142, 118, - 224, 10, 225, 58, 77, 210, 27, 66, 177, 165, 228, 40, 225, 211, 140, - 254, 31, 242, 230, 223, 21, 199, 221, 113, 146, 46, 213, 20, 63, - 148, 140, 144, 245, 105, 193, 124, 206, 235, 191, 252, 138, 155, - 148, 175, 185, 160, 98, 102, 156, 197, 29, 80, 202, 49, 26, 173, - 176, 53, 202, 13, 204, 180, 180, 190, 152, 223, 199, 65, 9, 173, 82, - 167, 12, 244, 127, 141, 8, 103, 155, 213, 2, 53, 83, 179, 157, 101, - 190, 205, 85, 58, 50, 89, 255, 11, 67, 18, 232, 252, 229, 197, 200, - 228, 130, 104, 250, 228, 19, 178, 183, 45, 156, 22, 73, 229, 170, - 163, 179, 116, 21, 149, 31, 81, 253, 100, 132, 46, 216, 143, 134, - 185, 96, 75, 57, 139, 21, 131, 114, 221, 124, 47, 104, 92, 235, 254, - 62, 69, 126, 117, 170, 141, 64, 121, 181, 101, 69, 135, 115, 102, - 74, 157, 233, 127, 139, 14, 79, 137, 156, 248, 117, 114, 205, 142, - 60, 8, 116, 77, 182, 28, 119, 149, 143, 252, 141, 46, 111, 100, 242, - 184, 21, 130, 61, 138, 27, 226, 70, 119, 195, 223, 180, 121 - ])); - }); - }); -} diff --git a/third_party/googleapis_auth/test/crypto/rsa_test.dart b/third_party/googleapis_auth/test/crypto/rsa_test.dart deleted file mode 100644 index de5ebf44..00000000 --- a/third_party/googleapis_auth/test/crypto/rsa_test.dart +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2014, the Dart 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 'package:googleapis_auth/src/crypto/rsa.dart'; -import 'package:test/test.dart'; - -import '../test_utils.dart'; - -/// 2 << 64 -final _bigNumber = BigInt.parse('20000000000000000', radix: 16); - -void main() { - group('rsa-algorithm', () { - test('integer-to-bytes', () { - expect(RSAAlgorithm.integer2Bytes(BigInt.one, 1), equals([1])); - expect(RSAAlgorithm.integer2Bytes(_bigNumber, 9), - equals([2, 0, 0, 0, 0, 0, 0, 0, 0])); - expect(RSAAlgorithm.integer2Bytes(_bigNumber, 12), - equals([0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0])); - expect(() => RSAAlgorithm.integer2Bytes(BigInt.zero, 1), - throwsA(isArgumentError)); - }); - - test('bytes-to-integer', () { - expect(RSAAlgorithm.bytes2BigInt([1]), equals(BigInt.one)); - expect( - RSAAlgorithm.bytes2BigInt([2, 0, 0, 0, 0, 0, 0, 0, 0]), _bigNumber); - }); - - test('encrypt', () { - final encryptedData = [ - 155, 24, 116, 247, 12, 118, 240, 206, 240, 138, 136, 193, 3, 73, //!! - 241, 63, 212, 100, 97, 46, 55, 113, 119, 95, 240, 219, 136, 211, 3, 4, - 43, 137, 213, 92, 233, 57, 172, 80, 179, 117, 83, 88, 249, 75, 17, 20, - 195, 51, 25, 97, 248, 217, 41, 117, 55, 63, 5, 252, 42, 133, 82, 73, 52, - 219, 255, 38, 137, 209, 83, 57, 245, 188, 180, 233, 249, 144, 100, 153, - 145, 14, 94, 2, 229, 165, 131, 178, 195, 178, 95, 244, 153, 196, 130, - 39, 158, 143, 98, 181, 223, 184, 68, 198, 201, 203, 89, 15, 41, 185, - 226, 64, 226, 161, 43, 228, 90, 58, 152, 203, 142, 133, 113, 120, 97, - 78, 149, 86, 214, 135, 29, 29, 190, 16, 47, 210, 1, 213, 86, 100, 116, - 187, 11, 255, 224, 6, 6, 206, 60, 138, 24, 179, 245, 248, 200, 45, 167, - 100, 78, 131, 204, 120, 22, 73, 116, 127, 65, 201, 15, 177, 250, 4, 73, - 245, 67, 119, 21, 54, 255, 227, 206, 37, 216, 13, 8, 109, 238, 215, 22, - 63, 163, 155, 33, 148, 254, 113, 17, 68, 65, 48, 82, 43, 240, 249, 87, - 19, 87, 162, 148, 169, 93, 22, 135, 125, 134, 187, 48, 93, 52, 20, 182, - 56, 93, 0, 175, 193, 213, 144, 29, 44, 240, 226, 91, 54, 178, 241, 240, - 85, 53, 148, 172, 138, 107, 131, 14, 157, 183, 137, 46, 130, 51, 233, - 26, 217, 230, 133, 217, 76 - ]; - expect( - RSAAlgorithm.encrypt( - testPrivateKey, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 256), - equals(encryptedData)); - }); - }); -} diff --git a/third_party/googleapis_auth/test/http_client_base_test.dart b/third_party/googleapis_auth/test/http_client_base_test.dart deleted file mode 100644 index 6181f0cc..00000000 --- a/third_party/googleapis_auth/test/http_client_base_test.dart +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; - -import 'package:googleapis_auth/src/auth_http_utils.dart'; -import 'package:googleapis_auth/src/http_client_base.dart'; -import 'package:http/http.dart'; -import 'package:test/test.dart'; - -import 'test_utils.dart'; - -class DelegatingClientImpl extends DelegatingClient { - DelegatingClientImpl(super.base, {required super.closeUnderlyingClient}); - - @override - Future send(BaseRequest request) => - throw UnsupportedError('Not supported'); -} - -final _defaultResponse = Response('', 500); - -Future _defaultResponseHandler(Request _) async => _defaultResponse; - -void main() { - group('http-utils', () { - group('delegating-client', () { - test('not-close-underlying-client', () { - final mock = mockClient(_defaultResponseHandler, expectClose: false); - DelegatingClientImpl(mock, closeUnderlyingClient: false).close(); - }); - - test('close-underlying-client', () { - final mock = mockClient(_defaultResponseHandler); - DelegatingClientImpl(mock, closeUnderlyingClient: true).close(); - }); - - test('close-several-times', () { - final mock = mockClient(_defaultResponseHandler); - final delegate = - DelegatingClientImpl(mock, closeUnderlyingClient: true); - delegate.close(); - expect(delegate.close, throwsA(isStateError)); - }); - }); - - group('refcounted-client', () { - test('not-close-underlying-client', () { - final mock = mockClient(_defaultResponseHandler, expectClose: false); - final client = RefCountedClient(mock, initialRefCount: 3); - client.close(); - client.close(); - }); - - test('close-underlying-client', () { - final mock = mockClient(_defaultResponseHandler); - final client = RefCountedClient(mock, initialRefCount: 3); - client.close(); - client.close(); - client.close(); - }); - - test('acquire-release', () { - final mock = mockClient(_defaultResponseHandler); - final client = RefCountedClient(mock); - client.acquire(); - client.release(); - client.acquire(); - client.release(); - client.release(); - }); - - test('close-several-times', () { - final mock = mockClient(_defaultResponseHandler); - final client = RefCountedClient(mock); - client.close(); - expect(client.close, throwsA(isStateError)); - }); - }); - - group('api-client', () { - const key = 'foo%?bar'; - final keyEncoded = 'key=${Uri.encodeQueryComponent(key)}'; - - RequestImpl request(String url) => RequestImpl('GET', Uri.parse(url)); - Future responseF() => - Future.value(Response.bytes([], 200)); - - test('no-query-string', () { - final mock = mockClient((Request request) { - expect('${request.url}', equals('http://localhost/abc?$keyEncoded')); - return responseF(); - }); - - final client = ApiKeyClient(mock, key); - expect(client.send(request('http://localhost/abc')), completes); - client.close(); - }); - - test('with-query-string', () { - final mock = mockClient((Request request) { - expect( - '${request.url}', equals('http://localhost/abc?x&$keyEncoded')); - return responseF(); - }); - - final client = ApiKeyClient(mock, key); - expect(client.send(request('http://localhost/abc?x')), completes); - client.close(); - }); - - test('with-existing-key', () { - final mock = - mockClient(expectAsync1(_defaultResponseHandler, count: 0)); - - final client = ApiKeyClient(mock, key); - expect(client.send(request('http://localhost/abc?key=a')), - throwsArgumentError); - client.close(); - }); - }); - - test('non-closing-client', () { - final mock = mockClient(_defaultResponseHandler, expectClose: false); - nonClosingClient(mock).close(); - }); - }); -} diff --git a/third_party/googleapis_auth/test/json_test.dart b/third_party/googleapis_auth/test/json_test.dart deleted file mode 100644 index 9e3e0283..00000000 --- a/third_party/googleapis_auth/test/json_test.dart +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2021, the Dart 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:convert'; - -import 'package:googleapis_auth/googleapis_auth.dart'; -import 'package:test/test.dart'; - -void main() { - test('AccessToken & AccessCredentials', () { - final credentials = AccessCredentials( - AccessToken('type', 'data', DateTime.now().toUtc()), - 'refreshToken', - ['scope1'], - idToken: 'idToken', - ); - - final encoded = jsonEncode(credentials); - - final decoded = AccessCredentials.fromJson( - jsonDecode(encoded) as Map, - ); - - expect(decoded.refreshToken, 'refreshToken'); - expect(decoded.idToken, 'idToken'); - expect(decoded.scopes, ['scope1']); - expect(decoded.accessToken.expiry, credentials.accessToken.expiry); - expect(decoded.accessToken.data, credentials.accessToken.data); - expect(decoded.accessToken.type, credentials.accessToken.type); - - expect(jsonEncode(decoded), encoded); - }); - - test('ClientId', () { - final clientId = ClientId('identifier', 'secret'); - - final encoded = jsonEncode(clientId); - - final decoded = ClientId.fromJson( - jsonDecode(encoded) as Map, - ); - - expect(decoded.identifier, clientId.identifier); - expect(decoded.secret, clientId.secret); - - expect(jsonEncode(decoded), encoded); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/auth_code_test.dart b/third_party/googleapis_auth/test/oauth2_flows/auth_code_test.dart deleted file mode 100644 index ca814720..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/auth_code_test.dart +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:googleapis_auth/googleapis_auth.dart'; -import 'package:googleapis_auth/src/known_uris.dart'; -import 'package:googleapis_auth/src/oauth2_flows/authorization_code_grant_manual_flow.dart'; -import 'package:googleapis_auth/src/oauth2_flows/authorization_code_grant_server_flow.dart'; -import 'package:http/http.dart'; -import 'package:test/test.dart'; - -import '../test_utils.dart'; - -typedef RequestHandler = Future Function(Request _); - -final _browserFlowRedirectMatcher = predicate((object) { - if (object.startsWith('redirect_uri=')) { - final url = Uri.parse( - Uri.decodeComponent(object.substring('redirect_uri='.length))); - expect(url.scheme, equals('http')); - expect(url.host, equals('localhost')); - return true; - } - return false; -}); - -void main() { - final clientId = ClientId('id', 'secret'); - final scopes = ['s1', 's2']; - final authEndpoints = GoogleAuthEndpoints(); - - // Validation + Responses from the authorization server. - - RequestHandler successFullResponse({required bool manual}) => - (Request request) async { - expect(request.method, equals('POST')); - expect(request.url, googleOauth2TokenEndpoint); - expect( - request.headers['content-type']!, - startsWith('application/x-www-form-urlencoded'), - ); - - final pairs = request.body.split('&'); - expect(pairs, hasLength(6)); - - expect( - pairs, - containsAll([ - 'grant_type=authorization_code', - 'code=mycode', - 'client_id=id', - 'client_secret=secret', - allOf( - startsWith('code_verifier='), - hasLength(142), // happens to be the output length as implemented! - ), - if (manual) 'redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob', - if (!manual) _browserFlowRedirectMatcher - ]), - ); - - final result = { - 'token_type': 'Bearer', - 'access_token': 'tokendata', - 'expires_in': 3600, - 'refresh_token': 'my-refresh-token', - 'id_token': 'my-id-token', - 'scope': 's1 s2', - }; - return Response( - jsonEncode(result), - 200, - headers: jsonContentType, - ); - }; - - Future invalidResponse(Request request) async { - // Missing expires_in field! - final result = { - 'token_type': 'Bearer', - 'access_token': 'tokendata', - 'refresh_token': 'my-refresh-token', - 'id_token': 'my-id-token', - }; - return Response(jsonEncode(result), 200, headers: jsonContentType); - } - - // Validation functions for user prompt and access credentials. - - void validateAccessCredentials(AccessCredentials credentials) { - expect(credentials.accessToken.data, equals('tokendata')); - expect(credentials.accessToken.type, equals('Bearer')); - expect(credentials.scopes, equals(['s1', 's2'])); - expect(credentials.refreshToken, equals('my-refresh-token')); - expect(credentials.idToken, equals('my-id-token')); - expectExpiryOneHourFromNow(credentials.accessToken); - } - - Uri validateUserPromptUri(String url, {bool manual = false}) { - final uri = Uri.parse(url); - expect(uri.scheme, googleOauth2AuthorizationEndpoint.scheme); - expect(uri.authority, googleOauth2AuthorizationEndpoint.authority); - expect(uri.path, googleOauth2AuthorizationEndpoint.path); - expect(uri.queryParameters, { - 'client_id': clientId.identifier, - 'response_type': 'code', - 'scope': 's1 s2', - 'redirect_uri': isNotEmpty, - 'code_challenge': hasLength(43), - 'code_challenge_method': 'S256', - if (!manual) 'state': hasLength(32), - }); - - final redirectUri = Uri.parse(uri.queryParameters['redirect_uri']!); - - if (manual) { - expect('$redirectUri', equals('urn:ietf:wg:oauth:2.0:oob')); - } else { - expect(uri.queryParameters['state'], isNotNull); - expect(redirectUri.scheme, equals('http')); - expect(redirectUri.host, equals('localhost')); - } - - return redirectUri; - } - - group('authorization-code-flow', () { - group('manual-copy-paste', () { - Future manualUserPrompt(String url) async { - validateUserPromptUri(url, manual: true); - return 'mycode'; - } - - test('successful', () async { - final flow = AuthorizationCodeGrantManualFlow( - authEndpoints, - clientId, - scopes, - mockClient(successFullResponse(manual: true), expectClose: false), - manualUserPrompt, - ); - validateAccessCredentials(await flow.run()); - }); - - test('user-exception', () async { - // We use a TransportException here for convenience. - Future manualUserPromptError(String url) => - Future.error(TransportException()); - - final flow = AuthorizationCodeGrantManualFlow( - authEndpoints, - clientId, - scopes, - mockClient(successFullResponse(manual: true), expectClose: false), - manualUserPromptError, - ); - await expectLater(flow.run(), throwsA(isTransportException)); - }); - - test('transport-exception', () async { - final flow = AuthorizationCodeGrantManualFlow( - authEndpoints, - clientId, - scopes, - transportFailure, - manualUserPrompt, - ); - await expectLater(flow.run(), throwsA(isTransportException)); - }); - - test('invalid-server-response', () async { - final flow = AuthorizationCodeGrantManualFlow( - authEndpoints, - clientId, - scopes, - mockClient(invalidResponse, expectClose: false), - manualUserPrompt, - ); - await expectLater(flow.run(), throwsA(isServerRequestFailedException)); - }); - }); - - group('http-server', () { - Future callRedirectionEndpoint(Uri authCodeCall) async { - final ioClient = HttpClient(); - - final closeMe = expectAsync0(ioClient.close); - - try { - final request = await ioClient.getUrl(authCodeCall); - final response = await request.close(); - await response.drain(); - } finally { - closeMe(); - } - } - - Future postToRedirectionEndpoint(Uri authCodeCall) async { - final ioClient = HttpClient(); - - final closeMe = expectAsync0(ioClient.close); - - try { - final request = await ioClient.postUrl(authCodeCall); - final response = await request.close(); - await response.drain(); - } finally { - closeMe(); - } - } - - void userPrompt(String url) { - final redirectUri = validateUserPromptUri(url); - final authCodeCall = Uri( - scheme: redirectUri.scheme, - host: redirectUri.host, - port: redirectUri.port, - path: redirectUri.path, - queryParameters: { - 'state': Uri.parse(url).queryParameters['state'], - 'code': 'mycode', - }); - callRedirectionEndpoint(authCodeCall); - } - - void userPromptInvalidHttpVerb(String url) { - final redirectUri = validateUserPromptUri(url); - final authCodeCall = Uri( - scheme: redirectUri.scheme, - host: redirectUri.host, - port: redirectUri.port, - path: redirectUri.path, - queryParameters: { - 'state': Uri.parse(url).queryParameters['state'], - 'code': 'mycode', - }); - postToRedirectionEndpoint(authCodeCall); - } - - void userPromptNonMatchingState(String url) { - final redirectUri = validateUserPromptUri(url); - final authCodeCall = Uri( - scheme: redirectUri.scheme, - host: redirectUri.host, - port: redirectUri.port, - path: redirectUri.path, - queryParameters: { - 'state': 'not-the-right-state', - 'code': 'mycode', - }); - callRedirectionEndpoint(authCodeCall); - } - - void userPromptInvalidAuthCodeCallback(String url) { - final redirectUri = validateUserPromptUri(url); - final authCodeCall = Uri( - scheme: redirectUri.scheme, - host: redirectUri.host, - port: redirectUri.port, - path: redirectUri.path, - queryParameters: { - 'state': Uri.parse(url).queryParameters['state'], - 'error': 'failed to authenticate', - }); - callRedirectionEndpoint(authCodeCall); - } - - test('successful', () async { - final flow = AuthorizationCodeGrantServerFlow( - authEndpoints, - clientId, - scopes, - mockClient(successFullResponse(manual: false), expectClose: false), - expectAsync1(userPrompt), - ); - validateAccessCredentials(await flow.run()); - }); - - test('transport-exception', () async { - final flow = AuthorizationCodeGrantServerFlow( - authEndpoints, - clientId, - scopes, - transportFailure, - expectAsync1(userPrompt), - ); - await expectLater(flow.run(), throwsA(isTransportException)); - }); - - test('non-GET request', () async { - final flow = AuthorizationCodeGrantServerFlow( - authEndpoints, - clientId, - scopes, - mockClient(successFullResponse(manual: false), expectClose: false), - expectAsync1(userPromptInvalidHttpVerb), - ); - await expectLater( - flow.run, - throwsA( - isA().having( - (e) => e.toString(), - 'message', - 'Exception: Invalid response from server (expected GET request callback, got: POST).', - ), - ), - ); - }); - - test('request with invalid state parameter', () async { - final flow = AuthorizationCodeGrantServerFlow( - authEndpoints, - clientId, - scopes, - mockClient(successFullResponse(manual: false), expectClose: false), - expectAsync1(userPromptNonMatchingState), - ); - await expectLater( - flow.run, - throwsA( - isA().having( - (e) => e.toString(), - 'message', - 'Exception: Invalid response from server (state did not match).', - ), - ), - ); - }); - - test('invalid-server-response', () async { - final flow = AuthorizationCodeGrantServerFlow( - authEndpoints, - clientId, - scopes, - mockClient(invalidResponse, expectClose: false), - expectAsync1(userPrompt), - ); - await expectLater(flow.run(), throwsA(isServerRequestFailedException)); - }); - - test('failed-authentication', () async { - final flow = AuthorizationCodeGrantServerFlow( - authEndpoints, - clientId, - scopes, - mockClient(successFullResponse(manual: false), expectClose: false), - expectAsync1(userPromptInvalidAuthCodeCallback), - ); - await expectLater(flow.run(), throwsA(isUserConsentException)); - }); - }, testOn: '!browser'); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_force.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_force.js deleted file mode 100644 index a2f2beb7..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_force.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -(function() { - // This function looks up the URL this script was loaded in and finds the - // name of the callback function to call when the library is read. - // The URL of the script load looks like: - // http://localhost:8080/folder/file?onload=dartGapiLoaded - function findDartOnLoadCallback() { - var scripts = document.getElementsByTagName('script'); - var self = scripts[scripts.length - 1]; - - var equalsSign = self.src.indexOf('='); - if (equalsSign <= 0) throw new Error('error'); - - var callbackName = self.src.substring(equalsSign + 1); - if (callbackName.length <= 0) throw new Error('error'); - - var dartFunction = window[callbackName]; - if (dartFunction == null) throw new Error('error'); - - return dartFunction; - } - - function GapiAuth() {} - GapiAuth.prototype.init = function(doneCallback) { - doneCallback(); - }; - GapiAuth.prototype.authorize = function(json, doneCallback) { - var client_id = json['client_id']; - var response_type = json['response_type']; - var scope = json['scope']; - var prompt = json['prompt']; - - if (client_id == 'foo_client' && - response_type == 'code token' && - scope == 'scope1 scope2' && - prompt == 'consent') { - doneCallback({ - 'token_type' : 'Bearer', - 'access_token' : 'foo_token', - 'expires_at' : Date.now() + 1000 * 3210, - 'code' : 'mycode', - 'scope': scope, - }); - } else { - throw new Error('error'); - } - }; - - // Initialize the gapi.auth mock. - window.gapi = new Object(); - window.gapi.auth2 = new GapiAuth(); - - // Call the dart function. This signals that gapi.auth was loaded. - var dartFunction = findDartOnLoadCallback(); - dartFunction(); -})(); diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_force_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_force_test.dart deleted file mode 100644 index fc9f91ba..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_force_test.dart +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -library; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - impl.gapiUrl = resource('gapi_auth_hybrid_force.js'); - - test('gapi-auth-hybrid-force-test', () async { - final clientId = auth.ClientId('foo_client', 'foo_secret'); - final scopes = ['scope1', 'scope2']; - - // ignore: deprecated_member_use_from_same_package - final flow = await auth.createImplicitBrowserFlow(clientId, scopes); - final result = await flow.runHybridFlow(); - - final credentials = result.credentials; - - final date = DateTime.now().toUtc().add(const Duration(seconds: 3210)); - final difference = credentials.accessToken.expiry.difference(date); - final seconds = difference.inSeconds; - - expect(seconds, inInclusiveRange(-3, 3)); - expect(credentials.accessToken.data, 'foo_token'); - expect(credentials.refreshToken, isNull); - expect(credentials.scopes, hasLength(2)); - expect(credentials.scopes[0], 'scope1'); - expect(credentials.scopes[1], 'scope2'); - - expect(result.authorizationCode, 'mycode'); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_immediate.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_immediate.js deleted file mode 100644 index 59a6c771..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_immediate.js +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -(function() { - // This function looks up the URL this script was loaded in and finds the - // name of the callback function to call when the library is read. - // The URL of the script load looks like: - // http://localhost:8080/folder/file?onload=dartGapiLoaded - function findDartOnLoadCallback() { - var scripts = document.getElementsByTagName('script'); - var self = scripts[scripts.length - 1]; - - var equalsSign = self.src.indexOf('='); - if (equalsSign <= 0) throw new Error('error'); - - var callbackName = self.src.substring(equalsSign + 1); - if (callbackName.length <= 0) throw new Error('error'); - - var dartFunction = window[callbackName]; - if (dartFunction == null) throw new Error('error'); - - return dartFunction; - } - - function GapiAuth() {} - GapiAuth.prototype.init = function(doneCallback) { - doneCallback(); - }; - GapiAuth.prototype.authorize = function(json, doneCallback) { - var client_id = json['client_id']; - var response_type = json['response_type']; - var scope = json['scope']; - - if (client_id == 'foo_client' && - response_type == 'code token' && - scope == 'scope1 scope2') { - doneCallback({ - 'token_type' : 'Bearer', - 'access_token' : 'foo_token', - 'expires_at' : Date.now() + 1000 * 3210, - 'code' : 'mycode', - 'scope': scope, - }); - } else { - throw new Error('error'); - } - }; - - // Initialize the gapi.auth mock. - window.gapi = new Object(); - window.gapi.auth2 = new GapiAuth(); - - // Call the dart function. This signals that gapi.auth was loaded. - var dartFunction = findDartOnLoadCallback(); - dartFunction(); -})(); diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_immediate_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_immediate_test.dart deleted file mode 100644 index 6752ce96..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_immediate_test.dart +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -library; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - impl.gapiUrl = resource('gapi_auth_hybrid_immediate.js'); - - test('gapi-auth-hybrid-immediate-test', () async { - final clientId = auth.ClientId('foo_client', 'foo_secret'); - final scopes = ['scope1', 'scope2']; - - // ignore: deprecated_member_use_from_same_package - final flow = await auth.createImplicitBrowserFlow(clientId, scopes); - final result = await flow.runHybridFlow(force: false, immediate: true); - final credentials = result.credentials; - - final date = DateTime.now().toUtc().add(const Duration(seconds: 3210)); - final difference = credentials.accessToken.expiry.difference(date); - final seconds = difference.inSeconds; - - expect(seconds, inInclusiveRange(-3, 3)); - expect(credentials.accessToken.data, 'foo_token'); - expect(credentials.refreshToken, isNull); - expect(credentials.scopes, hasLength(2)); - expect(credentials.scopes[0], 'scope1'); - expect(credentials.scopes[1], 'scope2'); - - expect(result.authorizationCode, 'mycode'); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_nonforce.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_nonforce.js deleted file mode 100644 index 59a6c771..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_nonforce.js +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -(function() { - // This function looks up the URL this script was loaded in and finds the - // name of the callback function to call when the library is read. - // The URL of the script load looks like: - // http://localhost:8080/folder/file?onload=dartGapiLoaded - function findDartOnLoadCallback() { - var scripts = document.getElementsByTagName('script'); - var self = scripts[scripts.length - 1]; - - var equalsSign = self.src.indexOf('='); - if (equalsSign <= 0) throw new Error('error'); - - var callbackName = self.src.substring(equalsSign + 1); - if (callbackName.length <= 0) throw new Error('error'); - - var dartFunction = window[callbackName]; - if (dartFunction == null) throw new Error('error'); - - return dartFunction; - } - - function GapiAuth() {} - GapiAuth.prototype.init = function(doneCallback) { - doneCallback(); - }; - GapiAuth.prototype.authorize = function(json, doneCallback) { - var client_id = json['client_id']; - var response_type = json['response_type']; - var scope = json['scope']; - - if (client_id == 'foo_client' && - response_type == 'code token' && - scope == 'scope1 scope2') { - doneCallback({ - 'token_type' : 'Bearer', - 'access_token' : 'foo_token', - 'expires_at' : Date.now() + 1000 * 3210, - 'code' : 'mycode', - 'scope': scope, - }); - } else { - throw new Error('error'); - } - }; - - // Initialize the gapi.auth mock. - window.gapi = new Object(); - window.gapi.auth2 = new GapiAuth(); - - // Call the dart function. This signals that gapi.auth was loaded. - var dartFunction = findDartOnLoadCallback(); - dartFunction(); -})(); diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_nonforce_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_nonforce_test.dart deleted file mode 100644 index b78765de..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_hybrid_nonforce_test.dart +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -library; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - impl.gapiUrl = resource('gapi_auth_hybrid_nonforce.js'); - - test('gapi-auth-hybrid-nonforce-test', () async { - final clientId = auth.ClientId('foo_client', 'foo_secret'); - final scopes = ['scope1', 'scope2']; - - // ignore: deprecated_member_use_from_same_package - final flow = await auth.createImplicitBrowserFlow(clientId, scopes); - final result = await flow.runHybridFlow(force: false); - final credentials = result.credentials; - - final date = DateTime.now().toUtc().add(const Duration(seconds: 3210)); - final difference = credentials.accessToken.expiry.difference(date); - final seconds = difference.inSeconds; - - expect(seconds, inInclusiveRange(-3, 3)); - expect(credentials.accessToken.data, 'foo_token'); - expect(credentials.refreshToken, isNull); - expect(credentials.scopes, hasLength(2)); - expect(credentials.scopes[0], 'scope1'); - expect(credentials.scopes[1], 'scope2'); - - expect(result.authorizationCode, 'mycode'); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_immediate.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_immediate.js deleted file mode 100644 index 994e82a3..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_immediate.js +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -(function() { - // This function looks up the URL this script was loaded in and finds the - // name of the callback function to call when the library is read. - // The URL of the script load looks like: - // http://localhost:8080/folder/file?onload=dartGapiLoaded - function findDartOnLoadCallback() { - var scripts = document.getElementsByTagName('script'); - var self = scripts[scripts.length - 1]; - - var equalsSign = self.src.indexOf('='); - if (equalsSign <= 0) throw new Error('error'); - - var callbackName = self.src.substring(equalsSign + 1); - if (callbackName.length <= 0) throw new Error('error'); - - var dartFunction = window[callbackName]; - if (dartFunction == null) throw new Error('error'); - - return dartFunction; - } - - function GapiAuth() {} - GapiAuth.prototype.init = function(doneCallback) { - doneCallback(); - }; - GapiAuth.prototype.authorize = function(json, doneCallback) { - var client_id = json['client_id']; - var response_type = json['response_type']; - var scope = json['scope']; - - if (client_id == 'foo_client' && - response_type == 'token' && - scope == 'scope1 scope2') { - doneCallback({ - 'token_type' : 'Bearer', - 'access_token' : 'foo_token', - 'expires_at' : Date.now() + 1000 * 3210, - 'scope': scope, - }); - } else { - throw new Error('error'); - } - }; - - // Initialize the gapi.auth mock. - window.gapi = new Object(); - window.gapi.auth2 = new GapiAuth(); - - // Call the dart function. This signals that gapi.auth was loaded. - var dartFunction = findDartOnLoadCallback(); - dartFunction(); -})(); diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_immediate_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_immediate_test.dart deleted file mode 100644 index 5d72e939..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_immediate_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -library; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - impl.gapiUrl = resource('gapi_auth_immediate.js'); - - test('gapi-auth-force', () async { - final clientId = auth.ClientId('foo_client', 'foo_secret'); - final scopes = ['scope1', 'scope2']; - - // ignore: deprecated_member_use_from_same_package - final flow = await auth.createImplicitBrowserFlow(clientId, scopes); - final credentials = - await flow.obtainAccessCredentialsViaUserConsent(immediate: true); - final date = DateTime.now().toUtc().add(const Duration(seconds: 3210)); - final difference = credentials.accessToken.expiry.difference(date); - final seconds = difference.inSeconds; - - expect(seconds, inInclusiveRange(-3, 3)); - expect(credentials.accessToken.data, 'foo_token'); - expect(credentials.refreshToken, isNull); - expect(credentials.scopes, hasLength(2)); - expect(credentials.scopes[0], 'scope1'); - expect(credentials.scopes[1], 'scope2'); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_implicit_idtoken.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_implicit_idtoken.js deleted file mode 100644 index b2b0fa6d..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_implicit_idtoken.js +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -(function () { - // This function looks up the URL this script was loaded in and finds the - // name of the callback function to call when the library is read. - // The URL of the script load looks like: - // http://localhost:8080/folder/file?onload=dartGapiLoaded - function findDartOnLoadCallback() { - var scripts = document.getElementsByTagName('script'); - var self = scripts[scripts.length - 1]; - - var equalsSign = self.src.indexOf('='); - if (equalsSign <= 0) throw new Error('error'); - - var callbackName = self.src.substring(equalsSign + 1); - if (callbackName.length <= 0) throw new Error('error'); - - var dartFunction = window[callbackName]; - if (dartFunction == null) throw new Error('error'); - - return dartFunction; - } - - function GapiAuth() { } - GapiAuth.prototype.init = function (doneCallback) { - doneCallback(); - }; - GapiAuth.prototype.authorize = function (json, doneCallback) { - var client_id = json['client_id']; - var response_type = json['response_type']; - var scope = json['scope']; - - if (client_id == 'foo_client' && - response_type == 'id_token token' && - scope == 'scope1 scope2') { - doneCallback({ - 'token_type': 'Bearer', - 'access_token': 'foo_token', - 'id_token': 'foo_id_token', - 'expires_at' : Date.now() + 1000 * 3210, - 'scope': scope, - }); - } else { - throw new Error('error'); - } - }; - - // Initialize the gapi.auth mock. - window.gapi = new Object(); - window.gapi.auth2 = new GapiAuth(); - - // Call the dart function. This signals that gapi.auth was loaded. - var dartFunction = findDartOnLoadCallback(); - dartFunction(); -})(); diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_implicit_idtoken_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_implicit_idtoken_test.dart deleted file mode 100644 index 08e0f760..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_implicit_idtoken_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -library; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - impl.gapiUrl = resource('gapi_auth_implicit_idtoken.js'); - - test('gapi-auth-implicit-idtoken', () async { - final clientId = auth.ClientId('foo_client', 'foo_secret'); - final scopes = ['scope1', 'scope2']; - - // ignore: deprecated_member_use_from_same_package - final flow = await auth.createImplicitBrowserFlow(clientId, scopes); - final credentials = await flow.obtainAccessCredentialsViaUserConsent( - responseTypes: [auth.ResponseType.idToken, auth.ResponseType.token]); - - final date = DateTime.now().toUtc().add(const Duration(seconds: 3210)); - final difference = credentials.accessToken.expiry.difference(date); - final seconds = difference.inSeconds; - - expect(seconds, inInclusiveRange(-3, 3)); - expect(credentials.accessToken.data, 'foo_token'); - expect(credentials.refreshToken, isNull); - expect(credentials.scopes, hasLength(2)); - expect(credentials.scopes[0], 'scope1'); - expect(credentials.scopes[1], 'scope2'); - expect(credentials.idToken, 'foo_id_token'); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_nonforce.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_nonforce.js deleted file mode 100644 index 994e82a3..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_nonforce.js +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -(function() { - // This function looks up the URL this script was loaded in and finds the - // name of the callback function to call when the library is read. - // The URL of the script load looks like: - // http://localhost:8080/folder/file?onload=dartGapiLoaded - function findDartOnLoadCallback() { - var scripts = document.getElementsByTagName('script'); - var self = scripts[scripts.length - 1]; - - var equalsSign = self.src.indexOf('='); - if (equalsSign <= 0) throw new Error('error'); - - var callbackName = self.src.substring(equalsSign + 1); - if (callbackName.length <= 0) throw new Error('error'); - - var dartFunction = window[callbackName]; - if (dartFunction == null) throw new Error('error'); - - return dartFunction; - } - - function GapiAuth() {} - GapiAuth.prototype.init = function(doneCallback) { - doneCallback(); - }; - GapiAuth.prototype.authorize = function(json, doneCallback) { - var client_id = json['client_id']; - var response_type = json['response_type']; - var scope = json['scope']; - - if (client_id == 'foo_client' && - response_type == 'token' && - scope == 'scope1 scope2') { - doneCallback({ - 'token_type' : 'Bearer', - 'access_token' : 'foo_token', - 'expires_at' : Date.now() + 1000 * 3210, - 'scope': scope, - }); - } else { - throw new Error('error'); - } - }; - - // Initialize the gapi.auth mock. - window.gapi = new Object(); - window.gapi.auth2 = new GapiAuth(); - - // Call the dart function. This signals that gapi.auth was loaded. - var dartFunction = findDartOnLoadCallback(); - dartFunction(); -})(); diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_nonforce_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_nonforce_test.dart deleted file mode 100644 index 81c7180f..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_nonforce_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -library; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - impl.gapiUrl = resource('gapi_auth_nonforce.js'); - - test('gapi-auth-nonforce', () async { - final clientId = auth.ClientId('foo_client', 'foo_secret'); - final scopes = ['scope1', 'scope2']; - - // ignore: deprecated_member_use_from_same_package - final flow = await auth.createImplicitBrowserFlow(clientId, scopes); - final credentials = await flow.obtainAccessCredentialsViaUserConsent(); - - final date = DateTime.now().toUtc().add(const Duration(seconds: 3210)); - final difference = credentials.accessToken.expiry.difference(date); - final seconds = difference.inSeconds; - - expect(seconds, inInclusiveRange(-3, 3)); - expect(credentials.accessToken.data, 'foo_token'); - expect(credentials.refreshToken, isNull); - expect(credentials.scopes, hasLength(2)); - expect(credentials.scopes[0], 'scope1'); - expect(credentials.scopes[1], 'scope2'); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_user_denied.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_user_denied.js deleted file mode 100644 index 035d7c24..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_user_denied.js +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -(function() { - // This function looks up the URL this script was loaded in and finds the - // name of the callback function to call when the library is read. - // The URL of the script load looks like: - // http://localhost:8080/folder/file?onload=dartGapiLoaded - function findDartOnLoadCallback() { - var scripts = document.getElementsByTagName('script'); - var self = scripts[scripts.length - 1]; - - var equalsSign = self.src.indexOf('='); - if (equalsSign <= 0) throw new Error('error'); - - var callbackName = self.src.substring(equalsSign + 1); - if (callbackName.length <= 0) throw new Error('error'); - - var dartFunction = window[callbackName]; - if (dartFunction == null) throw new Error('error'); - - return dartFunction; - } - - function GapiAuth() {} - GapiAuth.prototype.init = function(doneCallback) { - doneCallback(); - }; - GapiAuth.prototype.authorize = function(json, doneCallback) { - var client_id = json['client_id']; - var response_type = json['response_type']; - var scope = json['scope']; - - if (client_id == 'foo_client' && - response_type == 'token' && - scope == 'scope1 scope2') { - doneCallback({ - 'error' : 'failed to get user consent', - }); - } else { - throw new Error('error'); - } - }; - - // Initialize the gapi.auth mock. - window.gapi = new Object(); - window.gapi.auth2 = new GapiAuth(); - - // Call the dart function. This signals that gapi.auth was loaded. - var dartFunction = findDartOnLoadCallback(); - dartFunction(); -})(); \ No newline at end of file diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_user_denied_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_user_denied_test.dart deleted file mode 100644 index 7d895ff0..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_auth_user_denied_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -library; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - impl.gapiUrl = resource('gapi_auth_user_denied.js'); - - test('gapi-auth-user-denied', () async { - final clientId = auth.ClientId('foo_client', 'foo_secret'); - final scopes = ['scope1', 'scope2']; - - // ignore: deprecated_member_use_from_same_package - final flow = await auth.createImplicitBrowserFlow(clientId, scopes); - try { - await flow.obtainAccessCredentialsViaUserConsent(); - fail('expected error'); - } catch (error) { - expect(error is auth.UserConsentException, isTrue); - } - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_initialize_successful.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_initialize_successful.js deleted file mode 100644 index 1d728d77..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_initialize_successful.js +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -(function() { - // This function looks up the URL this script was loaded in and finds the - // name of the callback function to call when the library is read. - // The URL of the script load looks like: - // http://localhost:8080/folder/file?onload=dartGapiLoaded - function findDartOnLoadCallback() { - var scripts = document.getElementsByTagName('script'); - var self = scripts[scripts.length - 1]; - - var equalsSign = self.src.indexOf('='); - if (equalsSign <= 0) throw new Error('error'); - - var callbackName = self.src.substring(equalsSign + 1); - if (callbackName.length <= 0) throw new Error('error'); - - var dartFunction = window[callbackName]; - if (dartFunction == null) throw new Error('error'); - - return dartFunction; - } - - // Initialize the gapi.auth mock. - function GapiAuth() {} - GapiAuth.prototype.init = function (dartCallback) { - dartCallback(); - }; - window.gapi = new Object(); - window.gapi.auth = new GapiAuth(); - - // Call the dart function. This signals that gapi.auth was loaded. - var dartFunction = findDartOnLoadCallback(); - dartFunction(); -})(); \ No newline at end of file diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_initialize_successful_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_initialize_successful_test.dart deleted file mode 100644 index d93258c3..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_initialize_successful_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -library; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - impl.gapiUrl = resource('gapi_initialize_successful.js'); - - test('gapi-initialize-successful', () { - final clientId = auth.ClientId('a', 'b'); - final clientId2 = auth.ClientId('c', 'd'); - final scopes = ['scope1', 'scope2']; - - // ignore: deprecated_member_use_from_same_package - expect(auth.createImplicitBrowserFlow(clientId, scopes), completes); - // ignore: deprecated_member_use_from_same_package - expect(auth.createImplicitBrowserFlow(clientId2, scopes), completes); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_load_failure.js b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_load_failure.js deleted file mode 100644 index d83bbba6..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_load_failure.js +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -// We do not set 'window.gapi = ...' -this is a syntax error \ No newline at end of file diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_load_failure_test.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_load_failure_test.dart deleted file mode 100644 index 0cbc9186..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/gapi_load_failure_test.dart +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('browser') -@Timeout.factor(4) -library; - -import 'dart:html'; -import 'dart:js' as js; - -import 'package:googleapis_auth/auth_browser.dart' as auth; -import 'package:googleapis_auth/src/browser_utils.dart' as browser_utils; -import 'package:googleapis_auth/src/oauth2_flows/implicit.dart' as impl; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - test('gapi-load-failure', () { - impl.gapiUrl = resource('non_existent.js'); - expect( - // ignore: deprecated_member_use_from_same_package - auth.createImplicitBrowserFlow(_clientId, _scopes), - throwsA(isA()), - ); - }); - - test('gapi-load-failure--syntax-error', () async { - impl.gapiUrl = resource('gapi_load_failure.js'); - - // Reset test_controller.js's window.onerror registration. - // This makes sure we can catch the onError callback when the syntax error - // is produced. - js.context['onerror'] = null; - - window.onError.listen(expectAsync1((error) { - error.preventDefault(); - })); - - final sw = Stopwatch()..start(); - try { - // ignore: deprecated_member_use_from_same_package - await auth.createImplicitBrowserFlow(_clientId, _scopes); - fail('expected error'); - } catch (error) { - final elapsed = (sw.elapsed - browser_utils.callbackTimeout).inSeconds; - expect(elapsed, inInclusiveRange(-3, 3)); - } - }); -} - -final _clientId = auth.ClientId('a', 'b'); -const _scopes = ['scope1', 'scope2']; diff --git a/third_party/googleapis_auth/test/oauth2_flows/implicit/utils.dart b/third_party/googleapis_auth/test/oauth2_flows/implicit/utils.dart deleted file mode 100644 index a46dc710..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/implicit/utils.dart +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2014, the Dart 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:html'; - -String resource(String name) => - Uri.parse(document.baseUri!).resolve(name).toString(); diff --git a/third_party/googleapis_auth/test/oauth2_flows/jwt_test.dart b/third_party/googleapis_auth/test/oauth2_flows/jwt_test.dart deleted file mode 100644 index 9f80a546..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/jwt_test.dart +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; -import 'dart:convert'; - -import 'package:googleapis_auth/src/known_uris.dart'; -import 'package:googleapis_auth/src/oauth2_flows/jwt.dart'; -import 'package:http/http.dart'; -import 'package:test/test.dart'; - -import '../test_utils.dart'; - -void main() { - Future successfulSignRequest(Request request) async { - expect(request.method, equals('POST')); - expect(request.url, googleOauth2TokenEndpoint); - - // We are not asserting what comes after '&assertion=' because this is - // time dependent. - expect( - request.body, - startsWith( - 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer' - '&assertion=')); - final body = jsonEncode({ - 'access_token': 'atok', - 'expires_in': 3600, - 'token_type': 'Bearer', - }); - return Response(body, 200, headers: jsonContentType); - } - - Future invalidAccessToken(Request request) async { - final body = jsonEncode({ - // Missing 'expires_in' entry - 'access_token': 'atok', - 'token_type': 'Bearer', - }); - return Response(body, 200, headers: jsonContentType); - } - - group('jwt-flow', () { - const clientEmail = 'a@b.com'; - final scopes = ['s1', 's2']; - - test('successful', () async { - final flow = JwtFlow( - clientEmail, - testPrivateKey, - null, - scopes, - mockClient(expectAsync1(successfulSignRequest), expectClose: false), - ); - - final credentials = await flow.run(); - expect(credentials.accessToken.data, equals('atok')); - expect(credentials.accessToken.type, equals('Bearer')); - expect(credentials.scopes, equals(['s1', 's2'])); - expectExpiryOneHourFromNow(credentials.accessToken); - }); - - test('successfull-with-user', () async { - final flow = JwtFlow(clientEmail, testPrivateKey, 'x@y.com', scopes, - mockClient(expectAsync1(successfulSignRequest), expectClose: false)); - - final credentials = await flow.run(); - expect(credentials.accessToken.data, equals('atok')); - expect(credentials.accessToken.type, equals('Bearer')); - expect(credentials.scopes, equals(['s1', 's2'])); - expectExpiryOneHourFromNow(credentials.accessToken); - }); - - test('invalid-server-response', () { - final flow = JwtFlow( - clientEmail, - testPrivateKey, - null, - scopes, - mockClient(expectAsync1(invalidAccessToken), expectClose: false), - ); - - expect(flow.run(), throwsA(isServerRequestFailedException)); - }); - - test('transport-failure', () { - final flow = - JwtFlow(clientEmail, testPrivateKey, null, scopes, transportFailure); - - expect(flow.run(), throwsA(isTransportException)); - }); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_flows/metadata_server_test.dart b/third_party/googleapis_auth/test/oauth2_flows/metadata_server_test.dart deleted file mode 100644 index 9c638f10..00000000 --- a/third_party/googleapis_auth/test/oauth2_flows/metadata_server_test.dart +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2014, the Dart 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. - -@TestOn('vm') -library googleapis_auth.metadata_server; - -import 'dart:async'; -import 'dart:convert'; - -import 'package:googleapis_auth/src/oauth2_flows/metadata_server.dart'; -import 'package:http/http.dart'; -import 'package:test/test.dart'; - -import '../test_utils.dart'; - -void main() { - const apiUrl = 'http://metadata.google.internal/computeMetadata/v1'; - const apiHeaderKey = 'Metadata-Flavor'; - const apiHeaderValue = 'Google'; - const tokenUrl = '$apiUrl/instance/service-accounts/default/token'; - const scopesUrl = '$apiUrl/instance/service-accounts/default/scopes'; - - Future successfulAccessToken(Request request) async { - expect(request.method, equals('GET')); - expect(request.url.toString(), equals(tokenUrl)); - expect(request.headers[apiHeaderKey], equals(apiHeaderValue)); - - final body = jsonEncode({ - 'access_token': 'atok', - 'expires_in': 3600, - 'token_type': 'Bearer', - }); - return Response(body, 200, headers: jsonContentType); - } - - Future invalidAccessToken(Request request) async { - expect(request.method, equals('GET')); - expect(request.url.toString(), equals(tokenUrl)); - expect(request.headers[apiHeaderKey], equals(apiHeaderValue)); - - final body = jsonEncode({ - // Missing 'expires_in' entry - 'access_token': 'atok', - 'token_type': 'Bearer', - }); - return Response(body, 200, headers: jsonContentType); - } - - Future successfulScopes(Request request) { - expect(request.method, equals('GET')); - expect(request.url.toString(), equals(scopesUrl)); - expect(request.headers[apiHeaderKey], equals(apiHeaderValue)); - - return Future.value(Response('s1\ns2', 200)); - } - - group('metadata-server-authorization-flow', () { - test('successful', () async { - final flow = MetadataServerAuthorizationFlow(mockClient( - expectAsync1((request) { - final url = request.url.toString(); - if (url == tokenUrl) { - return successfulAccessToken(request); - } else if (url == scopesUrl) { - return successfulScopes(request); - } else { - fail('Invalid URL $url (expected: $tokenUrl or $scopesUrl).'); - } - }, count: 2), - expectClose: false)); - - final credentials = await flow.run(); - expect(credentials.accessToken.data, equals('atok')); - expect(credentials.accessToken.type, equals('Bearer')); - expect(credentials.scopes, equals(['s1', 's2'])); - expectExpiryOneHourFromNow(credentials.accessToken); - }); - - test('invalid-server-response', () { - var requestNr = 0; - final flow = MetadataServerAuthorizationFlow(mockClient( - expectAsync1((request) { - if (requestNr++ == 0) { - return invalidAccessToken(request); - } else { - return successfulScopes(request); - } - }, count: 2), - expectClose: false)); - expect(flow.run(), throwsA(isServerRequestFailedException)); - }); - - test('token-transport-error', () { - var requestNr = 0; - final flow = MetadataServerAuthorizationFlow(mockClient( - expectAsync1((request) { - if (requestNr++ == 0) { - // Dart 3 change that can't be fixed while we support Dart 2.x - // ignore: avoid_redundant_argument_values - return transportFailure.get(Uri.http('failure', '')); - } else { - return successfulScopes(request); - } - }, count: 2), - expectClose: false)); - expect(flow.run(), throwsA(isTransportException)); - }); - - test('scopes-transport-error', () { - var requestNr = 0; - final flow = MetadataServerAuthorizationFlow(mockClient( - expectAsync1((request) { - if (requestNr++ == 0) { - return successfulAccessToken(request); - } else { - // Dart 3 change that can't be fixed while we support Dart 2.x - // ignore: avoid_redundant_argument_values - return transportFailure.get(Uri.http('failure', '')); - } - }, count: 2), - expectClose: false)); - expect(flow.run(), throwsA(isTransportException)); - }); - }); -} diff --git a/third_party/googleapis_auth/test/oauth2_test.dart b/third_party/googleapis_auth/test/oauth2_test.dart deleted file mode 100644 index 9fb4c526..00000000 --- a/third_party/googleapis_auth/test/oauth2_test.dart +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright (c) 2014, the Dart 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:async'; -import 'dart:convert'; - -import 'package:googleapis_auth/googleapis_auth.dart'; -import 'package:googleapis_auth/src/http_client_base.dart'; -import 'package:googleapis_auth/src/known_uris.dart'; -import 'package:googleapis_auth/src/utils.dart'; -import 'package:http/http.dart'; -import 'package:test/test.dart'; - -import 'test_utils.dart'; - -final _defaultResponse = Response('', 500); - -Future _defaultResponseHandler(Request _) async => _defaultResponse; - -void main() { - final authEndpoints = GoogleAuthEndpoints(); - - test('access-token', () { - final expiry = DateTime.now().subtract(const Duration(seconds: 1)); - final expiryUtc = expiry.toUtc(); - - expect(() => AccessToken('foo', 'bar', expiry), throwsArgumentError); - - final token = AccessToken('foo', 'bar', expiryUtc); - expect(token.type, equals('foo')); - expect(token.data, equals('bar')); - expect(token.expiry, equals(expiryUtc)); - expect(token.hasExpired, isTrue); - - final nonExpiredToken = - AccessToken('foo', 'bar', expiryUtc.add(const Duration(days: 1))); - expect(nonExpiredToken.hasExpired, isFalse); - }); - - test('access-credentials', () { - final expiry = DateTime.now().add(const Duration(days: 1)).toUtc(); - final aToken = AccessToken('foo', 'bar', expiry); - - final credentials = AccessCredentials(aToken, 'refresh', ['scope']); - expect(credentials.accessToken, equals(aToken)); - expect(credentials.refreshToken, equals('refresh')); - expect(credentials.scopes, equals(['scope'])); - }); - - test('client-id', () { - final clientId = ClientId('id', 'secret'); - expect(clientId.identifier, equals('id')); - expect(clientId.secret, equals('secret')); - }); - - group('service-account-credentials', () { - final clientId = ClientId.serviceAccount('id'); - - const credentials = { - 'private_key_id': '301029', - 'private_key': testPrivateKeyString, - 'client_email': 'a@b.com', - 'client_id': 'myid', - 'type': 'service_account' - }; - - test('from-valid-individual-params', () { - final credentials = - ServiceAccountCredentials('email', clientId, testPrivateKeyString); - expect(credentials.email, equals('email')); - expect(credentials.clientId, equals(clientId)); - expect(credentials.privateKey, equals(testPrivateKeyString)); - expect(credentials.impersonatedUser, isNull); - }); - - test('from-valid-individual-params-with-user', () { - final credentials = ServiceAccountCredentials( - 'email', clientId, testPrivateKeyString, - impersonatedUser: 'x@y.com'); - expect(credentials.email, equals('email')); - expect(credentials.clientId, equals(clientId)); - expect(credentials.privateKey, equals(testPrivateKeyString)); - expect(credentials.impersonatedUser, equals('x@y.com')); - }); - - test('from-json-string', () { - final credentialsFromJson = - ServiceAccountCredentials.fromJson(jsonEncode(credentials)); - expect(credentialsFromJson.email, equals('a@b.com')); - expect(credentialsFromJson.clientId.identifier, equals('myid')); - expect(credentialsFromJson.clientId.secret, isNull); - expect(credentialsFromJson.privateKey, equals(testPrivateKeyString)); - expect(credentialsFromJson.impersonatedUser, isNull); - }); - - test('from-json-string-with-user', () { - final credentialsFromJson = ServiceAccountCredentials.fromJson( - jsonEncode(credentials), - impersonatedUser: 'x@y.com'); - expect(credentialsFromJson.email, equals('a@b.com')); - expect(credentialsFromJson.clientId.identifier, equals('myid')); - expect(credentialsFromJson.clientId.secret, isNull); - expect(credentialsFromJson.privateKey, equals(testPrivateKeyString)); - expect(credentialsFromJson.impersonatedUser, equals('x@y.com')); - }); - - test('from-json-map', () { - final credentialsFromJson = - ServiceAccountCredentials.fromJson(credentials); - expect(credentialsFromJson.email, equals('a@b.com')); - expect(credentialsFromJson.clientId.identifier, equals('myid')); - expect(credentialsFromJson.clientId.secret, isNull); - expect(credentialsFromJson.privateKey, equals(testPrivateKeyString)); - expect(credentialsFromJson.impersonatedUser, isNull); - }); - - test('from-json-map-with-user', () { - final credentialsFromJson = ServiceAccountCredentials.fromJson( - credentials, - impersonatedUser: 'x@y.com'); - expect(credentialsFromJson.email, equals('a@b.com')); - expect(credentialsFromJson.clientId.identifier, equals('myid')); - expect(credentialsFromJson.clientId.secret, isNull); - expect(credentialsFromJson.privateKey, equals(testPrivateKeyString)); - expect(credentialsFromJson.impersonatedUser, equals('x@y.com')); - }); - }); - - group('client-wrappers', () { - final clientId = ClientId('id', 'secret'); - final tomorrow = DateTime.now().add(const Duration(days: 1)).toUtc(); - final yesterday = DateTime.now().subtract(const Duration(days: 1)).toUtc(); - final aToken = AccessToken('Bearer', 'bar', tomorrow); - final credentials = AccessCredentials(aToken, 'refresh', ['s1', 's2']); - - Future successfulRefresh(Request request) async { - expect(request.method, equals('POST')); - expect(request.url, googleOauth2TokenEndpoint); - expect( - request.body, - equals( - 'client_id=id&' - 'client_secret=secret&' - 'refresh_token=refresh&' - 'grant_type=refresh_token', - ), - ); - final body = jsonEncode({ - 'token_type': 'Bearer', - 'access_token': 'atoken', - 'expires_in': 3600, - }); - - return Response(body, 200, headers: jsonContentType); - } - - Future refreshErrorResponse(Request request) async { - final body = jsonEncode({'error': 'An error occurred'}); - return Response(body, 400, headers: jsonContentType); - } - - Future serverError(Request request) => - Future.error(Exception('transport layer exception')); - - test('refreshCredentials-successful', () async { - final newCredentials = await refreshCredentials( - authEndpoints, - clientId, - credentials, - mockClient(expectAsync1(successfulRefresh), expectClose: false), - ); - final expectedResultUtc = DateTime.now() - .toUtc() - .add(const Duration(seconds: 3600 - maxExpectedTimeDiffInSeconds)); - - final accessToken = newCredentials.accessToken; - expect(accessToken.type, equals('Bearer')); - expect(accessToken.data, equals('atoken')); - expect(accessToken.expiry.difference(expectedResultUtc).inSeconds, - equals(0)); - - expect(newCredentials.refreshToken, equals('refresh')); - expect(newCredentials.scopes, equals(['s1', 's2'])); - }); - - test('refreshCredentials-http-error', () async { - await expectLater( - refreshCredentials( - authEndpoints, - clientId, - credentials, - mockClient(serverError, expectClose: false), - ), - throwsA( - isA().having( - (p0) => p0.toString(), - 'toString', - 'Exception: transport layer exception', - ), - ), - ); - }); - - test('refreshCredentials-error-response', () async { - await expectLater( - refreshCredentials( - authEndpoints, - clientId, - credentials, - mockClient(refreshErrorResponse, expectClose: false), - ), - throwsA(isServerRequestFailedException), - ); - }); - - group('authenticatedClient', () { - final url = Uri.parse('http://www.example.com'); - - test('successful', () async { - final client = authenticatedClient( - mockClient(expectAsync1((request) async { - expect(request.method, equals('POST')); - expect(request.url, equals(url)); - expect(request.headers.length, equals(1)); - expect(request.headers['Authorization'], equals('Bearer bar')); - - return Response('', 204); - }), expectClose: false), - credentials, - ); - expect(client.credentials, equals(credentials)); - - final response = await client.send(RequestImpl('POST', url)); - expect(response.statusCode, equals(204)); - }); - - test('access-denied', () { - final client = authenticatedClient( - mockClient(expectAsync1((request) async { - expect(request.method, equals('POST')); - expect(request.url, equals(url)); - expect(request.headers.length, equals(1)); - expect(request.headers['Authorization'], equals('Bearer bar')); - - const headers = {'www-authenticate': 'foobar'}; - return Response('', 401, headers: headers); - }), expectClose: false), - credentials, - ); - expect(client.credentials, equals(credentials)); - - expect(client.send(RequestImpl('POST', url)), - throwsA(isAccessDeniedException)); - }); - - test('non-bearer-token', () { - final aToken = credentials.accessToken; - final nonBearerCredentials = AccessCredentials( - AccessToken('foobar', aToken.data, aToken.expiry), - 'refresh', - ['s1', 's2']); - - expect( - () => authenticatedClient( - mockClient(_defaultResponseHandler, expectClose: false), - nonBearerCredentials, - ), - throwsA(isArgumentError), - ); - }); - }); - - group('autoRefreshingClient', () { - final url = Uri.parse('http://www.example.com'); - - test('up-to-date', () async { - final client = autoRefreshingClient( - authEndpoints, - clientId, - credentials, - mockClient( - expectAsync1((request) async => Response('', 200)), - expectClose: false, - ), - ); - expect(client.credentials, equals(credentials)); - - final response = await client.send(RequestImpl('POST', url)); - expect(response.statusCode, equals(200)); - }); - - test('no-refresh-token', () { - final credentials = AccessCredentials( - AccessToken('Bearer', 'bar', yesterday), null, ['s1', 's2']); - - expect( - () => autoRefreshingClient( - authEndpoints, - clientId, - credentials, - mockClient(_defaultResponseHandler, expectClose: false), - ), - throwsA(isArgumentError), - ); - }); - - test('refresh-failed', () { - final credentials = AccessCredentials( - AccessToken('Bearer', 'bar', yesterday), 'refresh', ['s1', 's2']); - - final client = autoRefreshingClient( - authEndpoints, - clientId, - credentials, - mockClient(expectAsync1((request) { - // This should be a refresh request. - expect(request.headers['foo'], isNull); - return refreshErrorResponse(request); - }), expectClose: false), - ); - expect(client.credentials, equals(credentials)); - - final request = RequestImpl('POST', url); - request.headers.addAll({'foo': 'bar'}); - expect(client.send(request), throwsA(isServerRequestFailedException)); - }); - - test('invalid-content-type', () { - final credentials = AccessCredentials( - AccessToken('Bearer', 'bar', yesterday), 'refresh', ['s1', 's2']); - - final client = autoRefreshingClient( - authEndpoints, - clientId, - credentials, - mockClient(expectAsync1((request) async { - // This should be a refresh request. - expect(request.headers['foo'], isNull); - final headers = {'content-type': 'image/png'}; - - return Response('', 200, headers: headers); - }), expectClose: false), - ); - expect(client.credentials, equals(credentials)); - - final request = RequestImpl('POST', url); - request.headers.addAll({'foo': 'bar'}); - expect(client.send(request), throwsA(isServerRequestFailedException)); - }); - - test('successful-refresh', () async { - var serverInvocation = 0; - - final credentials = AccessCredentials( - AccessToken('Bearer', 'bar', yesterday), 'refresh', ['s1']); - - final client = autoRefreshingClient( - authEndpoints, - clientId, - credentials, - mockClient( - expectAsync1( - (request) async { - if (serverInvocation++ == 0) { - // This should be a refresh request. - expect(request.headers['foo'], isNull); - return successfulRefresh(request); - } else { - // This is the real request. - expect(request.headers['foo'], equals('bar')); - return Response('', 200); - } - }, - count: 2, - ), - )); - expect(client.credentials, equals(credentials)); - - var executed = false; - client.credentialUpdates.listen( - expectAsync1((newCredentials) { - expect(newCredentials.accessToken.type, equals('Bearer')); - expect(newCredentials.accessToken.data, equals('atoken')); - executed = true; - }), - onDone: expectAsync0(() {}), - ); - - final request = RequestImpl('POST', url); - request.headers.addAll({'foo': 'bar'}); - - final response = await client.send(request); - expect(response.statusCode, equals(200)); - - // The `client.send()` will have triggered a credentials refresh. - expect(executed, isTrue); - - client.close(); - }); - }); - }); -} diff --git a/third_party/googleapis_auth/test/src/service_account_credentials_test.dart b/third_party/googleapis_auth/test/src/service_account_credentials_test.dart deleted file mode 100644 index 95950c42..00000000 --- a/third_party/googleapis_auth/test/src/service_account_credentials_test.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:googleapis_auth/src/service_account_credentials.dart'; -import 'package:test/test.dart'; - -void main() { - group(ServiceAccountCredentials, () { - group('fromJson', () { - test('throws exception if json is not a map', () { - expect( - () => ServiceAccountCredentials.fromJson('[1,2,3]'), - throwsArgumentError, - ); - }); - - test('throws exception if json is not a service account', () { - expect( - () => ServiceAccountCredentials.fromJson({ - 'type': 'not_service_account', - 'client_id': 'client_id', - 'private_key': 'private_key', - 'client_email': 'client_email', - }), - throwsArgumentError, - ); - }); - - test('throws exception if json is missing fields', () { - expect( - () => ServiceAccountCredentials.fromJson({ - 'type': 'service_account', - 'client_id': 'client_id', - }), - throwsArgumentError, - ); - }); - }); - }); -} diff --git a/third_party/googleapis_auth/test/test_utils.dart b/third_party/googleapis_auth/test/test_utils.dart deleted file mode 100644 index 93b5af81..00000000 --- a/third_party/googleapis_auth/test/test_utils.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2014, the Dart 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 'package:googleapis_auth/googleapis_auth.dart'; -import 'package:googleapis_auth/src/crypto/pem.dart'; -import 'package:googleapis_auth/src/utils.dart'; -import 'package:http/http.dart'; -import 'package:http/testing.dart'; -import 'package:test/test.dart'; - -const jsonContentType = {'content-type': 'application/json'}; - -const isServerRequestFailedException = - TypeMatcher(); - -const Matcher isUserConsentException = TypeMatcher(); - -const Matcher isAccessDeniedException = TypeMatcher(); - -const Matcher isTransportException = TypeMatcher(); - -class TransportException implements Exception {} - -Client get transportFailure => - MockClient(expectAsync1((_) async => throw TransportException())); - -const testPrivateKeyString = '''-----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAuDOwXO14ltE1j2O0iDSuqtbw/1kMKjeiki3oehk2zNoUte42 -/s2rX15nYCkKtYG/r8WYvKzb31P4Uow1S4fFydKNWxgX4VtEjHgeqfPxeCL9wiJc -9KkEt4fyhj1Jo7193gCLtovLAFwPzAMbFLiXWkfqalJ5Z77fOE4Mo7u4pEgxNPgL -VFGe0cEOAsHsKlsze+m1pmPHwWNVTcoKe5o0hOzy6hCPgVc6me6Y7aO8Fb4OVg0l -XQdQpWn2ikVBpzBcZ6InnYyJ/CJNa3WL1LJ65mmYnfHtKGoMqhLK48OReguwRwwF -e9/2+8UcdZcN5rsvt7yg3ZrKNH8rx+wZ36sRewIDAQABAoIBAQCn1HCcOsHkqDlk -rDOQ5m8+uRhbj4bF8GrvRWTL2q1TeF/mY2U4Q6wg+KK3uq1HMzCzthWz0suCb7+R -dq4YY1ySxoSEuy8G5WFPmyJVNy6Lh1Yty6FmSZlCn1sZdD3kMoK8A0NIz5Xmffrm -pu3Fs2ozl9K9jOeQ3xgC9RoPFLrm8lHJ45Vn+SnTxZnsXT6pwpg3TnFIx5ZinU8k -l0Um1n80qD2QQDakQ5jyr2odAELLBDlyCkxAglBXAVt4nk9Kl6nxb4snd9dnrL70 -WjLynWQsDczaV9TZIl2hYkMud+9OLVlUUtB+0c5b0p2t2P0sLltDaq3H6pT6yu2G -8E86J9IBAoGBAPJaTNV5ysVOFn+YwWwRztzrvNArUJkVq8abN0gGp3gUvDEZnvzK -weF7+lfZzcwVRmQkL3mWLzzZvCx77RfulAzLi5iFuRBPhhhxAPDiDuyL9B7O81G/ -M/W5DPctGOyD/9cnLuh72oij0unc5MLSfzJf8wblpcjJnPBDqIVh6Qt9AoGBAMKT -Gacf4iSj1xW+0wrnbZlDuyCl6Msptj8ePcvLQrFqQmBwsXmWgVR+gFc/1G3lRft0 -QC6chsmafQHIIPpaDjq3sQ01/tUu7LXL+g/Hw9XtUHbkg3sZIQBtC26rKdStfHNS -KTvuCgn/dAJNjiohfhWMt9R4Q6E5FV6PqQHJzPJXAoGAC41qZDKuC8GxKNvrPG+M -4NML6RBngySZT5pOhExs5zh10BFclshDfbAfOtjTCotpE5T1/mG+VrQ6WBSANMfW -ntWFDfwx2ikwRzH7zX+5HmV9eYp75sWqgGgVyiKIMZ4JMARaJBLjU+gbQbKZ5P+L -uKcCOq3vvSZ/KKTQ/6qvJTECgYBiWgbCgoxF5wdmd4Gn5llw+lqRYyur3hbACuJD -rCe3FDYfF3euNRSEiDkJYTtYnWbldtqmdPpw14VOrEF3KqQ8q/Nz8RIx4jlGn6dz -6I8mCIH+xv1q8MXMuFHqC9zmIxdgF2y+XVF3wkd6jodI5oscC3g0juHokbkqhkVw -oPfWmwKBgBfR6jv0gWWeWTfkNwj+cMLHQV1uvz6JyLH5K4iISEDFxYkd37jrHB8A -9hz9UDfmCbSs2j8CXDg7zCayM6tfu4Vtx+8S5g3oN6sa1JXFY1Os7SoXhTfX9M+7 -QpYYDJZwkgZrVQoKMIdCs9xfyVhZERq945NYLekwE1t2W+tOVBgR ------END RSA PRIVATE KEY-----'''; - -final testPrivateKey = keyFromString(testPrivateKeyString); - -void expectExpiryOneHourFromNow(AccessToken accessToken) { - final now = DateTime.now().toUtc(); - final diff = accessToken.expiry.difference(now).inSeconds - - (3600 - maxExpectedTimeDiffInSeconds); - expect(-2 <= diff && diff <= 2, isTrue); -} - -Client mockClient( - MockClientHandler requestHandler, { - bool expectClose = true, -}) => - ExpectCloseMockClient(requestHandler, expectClose ? 1 : 0); - -/// A client which will keep the VM alive until `close()` was called. -class ExpectCloseMockClient extends MockClient { - late void Function() _expectedToBeCalled; - - ExpectCloseMockClient( - super.requestHandler, - int c, - ) { - _expectedToBeCalled = expectAsync0(() {}, count: c); - } - - @override - void close() { - super.close(); - _expectedToBeCalled(); - } -} diff --git a/third_party/googleapis_auth/test/util_test.dart b/third_party/googleapis_auth/test/util_test.dart deleted file mode 100644 index dfba4f5b..00000000 --- a/third_party/googleapis_auth/test/util_test.dart +++ /dev/null @@ -1,111 +0,0 @@ -@Timeout(Duration(seconds: 2)) -library; - -import 'dart:convert'; - -import 'package:googleapis_auth/src/utils.dart'; -import 'package:http/http.dart'; -import 'package:test/test.dart'; - -import 'test_utils.dart'; - -void main() { - test('not valid UTF-8', () async { - const body = [ - // https://man7.org/linux/man-pages/man7/utf-8.7.html - // 0xC0 is never used in UTF8-encoding! - 0xC0, - ]; - final client = mockClient( - (request) async => Response.bytes(body, 200, headers: jsonContentType), - expectClose: false, - ); - - await expectLater( - client.requestJson(Request('GET', Uri.parse('localhost:8080')), 'bob'), - throwsA( - isServerRequestFailedException - .having( - (p0) => p0.message, - 'message', - contains('The response was not valid UTF-8.'), - ) - .having( - (p0) => p0.responseContent, - 'responseContent', - body, - ), - ), - ); - }); - - test('not JSON', () async { - const body = 'this is not good json!'; - final client = mockClient( - (request) async => Response(body, 200, headers: jsonContentType), - expectClose: false, - ); - - await expectLater( - client.requestJson(Request('GET', Uri.parse('localhost:8080')), 'bob'), - throwsA( - isServerRequestFailedException - .having( - (p0) => p0.message, - 'message', - contains('Could not decode the response as JSON.'), - ) - .having( - (p0) => p0.responseContent, - 'responseContent', - body, - ), - ), - ); - }); - - test('not a map', () async { - final body = []; - final client = mockClient( - (request) async => - Response(jsonEncode(body), 200, headers: jsonContentType), - expectClose: false, - ); - - await expectLater( - client.requestJson(Request('GET', Uri.parse('localhost:8080')), 'bob'), - throwsA( - isServerRequestFailedException - .having( - (p0) => p0.message, - 'message', - 'The returned JSON response was not a Map.', - ) - .having( - (p0) => p0.responseContent, - 'responseContent', - body, - ), - ), - ); - }); - - test('invalid-server-status-code', () async { - final client = mockClient( - (request) async => - Response(jsonEncode({}), 500, headers: jsonContentType), - expectClose: false, - ); - - await expectLater( - client.requestJson(Request('GET', Uri.parse('localhost:8080')), 'bob'), - throwsA( - isServerRequestFailedException.having( - (p0) => p0.statusCode, - 'statusCode', - 500, - ), - ), - ); - }); -}