From 61df84d1bdbd45c04b58e63c351b355227a6899e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Powa=C5=82owski?= Date: Thu, 6 Jul 2023 07:42:17 +0200 Subject: [PATCH] [google_sign_in_web] Fixes force unwrap on values that can be null (#4374) During Google Sign-in, the code uses two force unwraps on values (name and picture) that can be not present in the response. This cause an unhandled error that blocks sign-in. Fixes https://github.com/flutter/flutter/issues/130002 reported by me. The bug report describes how to get that error together with a screenshot of a given line. My PR fixes that and add additional test for the future. --- .../google_sign_in_web/CHANGELOG.md | 4 ++++ .../integration_test/src/jwt_examples.dart | 22 +++++++++++++++++++ .../example/integration_test/utils_test.dart | 19 ++++++++++++++++ .../google_sign_in_web/lib/src/utils.dart | 4 ++-- .../google_sign_in_web/pubspec.yaml | 2 +- 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index 363226ba25..7d39cd63b7 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.0+3 + +* Fixes null cast error on accounts without picture or name details. + ## 0.12.0+2 * Adds compatibility with `http` 1.0. diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/jwt_examples.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/jwt_examples.dart index 72841c5165..336b626c11 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/jwt_examples.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/jwt_examples.dart @@ -18,6 +18,12 @@ final CredentialResponse goodCredential = 'credential': goodJwtToken, }); +/// A CredentialResponse wrapping a known good JWT Token as its `credential`. +final CredentialResponse minimalCredential = + jsifyAs({ + 'credential': minimalJwtToken, +}); + /// A JWT token with predefined values. /// /// 'email': 'adultman@example.com', @@ -38,6 +44,22 @@ const String goodJwtToken = const String goodPayload = 'eyJlbWFpbCI6ImFkdWx0bWFuQGV4YW1wbGUuY29tIiwic3ViIjoiMTIzNDU2IiwibmFtZSI6IlZpbmNlbnQgQWR1bHRtYW4iLCJwaWN0dXJlIjoiaHR0cHM6Ly90aGlzcGVyc29uZG9lc25vdGV4aXN0LmNvbS9pbWFnZT94PS5qcGcifQ'; +/// A JWT token with minimal set of predefined values. +/// +/// 'email': 'adultman@example.com', +/// 'sub': '123456' +/// +/// Signed with HS256 and the private key: 'symmetric-encryption-is-weak' +const String minimalJwtToken = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.$minimalPayload.UTAe7dpdtFIMwsOqkZkjyjqyHnho5xHCcQylUFmOutM'; + +/// The payload of a JWT token that contains only non-nullable values. +/// +/// "email": "adultman@example.com", +/// "sub": "123456" +const String minimalPayload = + 'eyJlbWFpbCI6ImFkdWx0bWFuQGV4YW1wbGUuY29tIiwic3ViIjoiMTIzNDU2In0'; + // More encrypted JWT Tokens may be created on https://jwt.io. // // First, decode the `goodJwtToken` above, modify to your heart's diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/utils_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/utils_test.dart index 82701e587b..6f5fcfd97e 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/utils_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/utils_test.dart @@ -57,6 +57,17 @@ void main() { expect(data.idToken, goodJwtToken); }); + testWidgets('happy case (minimal)', (_) async { + final GoogleSignInUserData data = + gisResponsesToUserData(minimalCredential)!; + + expect(data.displayName, isNull); + expect(data.id, '123456'); + expect(data.email, 'adultman@example.com'); + expect(data.photoUrl, isNull); + expect(data.idToken, minimalJwtToken); + }); + testWidgets('null response -> null', (_) async { expect(gisResponsesToUserData(null), isNull); }); @@ -90,6 +101,14 @@ void main() { )); }); + testWidgets('happy case (minimal) -> data', (_) async { + final Map? data = getJwtTokenPayload(minimalJwtToken); + + expect(data, isNotNull); + expect(data, containsPair('email', 'adultman@example.com')); + expect(data, containsPair('sub', '123456')); + }); + testWidgets('null Token -> null', (_) async { final Map? data = getJwtTokenPayload(null); diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart index c4bb9d403d..8ec9cb5720 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart @@ -72,8 +72,8 @@ GoogleSignInUserData? gisResponsesToUserData( return GoogleSignInUserData( email: payload['email']! as String, id: payload['sub']! as String, - displayName: payload['name']! as String, - photoUrl: payload['picture']! as String, + displayName: payload['name'] as String?, + photoUrl: payload['picture'] as String?, idToken: credentialResponse.credential, ); } diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 9a2847fcff..0accb273f2 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.12.0+2 +version: 0.12.0+3 environment: sdk: ">=2.18.0 <4.0.0"