blob: c4bb9d403d2d46239e83d75b0b2b9c3ca3206fee [file] [log] [blame]
// Copyright 2013 The Flutter Authors. 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:google_identity_services_web/id.dart';
import 'package:google_identity_services_web/oauth2.dart';
import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart';
/// A codec that can encode/decode JWT payloads.
///
/// See https://www.rfc-editor.org/rfc/rfc7519#section-3
final Codec<Object?, String> jwtCodec = json.fuse(utf8).fuse(base64);
/// A RegExp that can match, and extract parts from a JWT Token.
///
/// A JWT token consists of 3 base-64 encoded parts of data separated by periods:
///
/// header.payload.signature
///
/// More info: https://regexr.com/789qc
final RegExp jwtTokenRegexp = RegExp(
r'^(?<header>[^\.\s]+)\.(?<payload>[^\.\s]+)\.(?<signature>[^\.\s]+)$');
/// Decodes the `claims` of a JWT token and returns them as a Map.
///
/// JWT `claims` are stored as a JSON object in the `payload` part of the token.
///
/// (This method does not validate the signature of the token.)
///
/// See https://www.rfc-editor.org/rfc/rfc7519#section-3
Map<String, Object?>? getJwtTokenPayload(String? token) {
if (token != null) {
final RegExpMatch? match = jwtTokenRegexp.firstMatch(token);
if (match != null) {
return decodeJwtPayload(match.namedGroup('payload'));
}
}
return null;
}
/// Decodes a JWT payload using the [jwtCodec].
Map<String, Object?>? decodeJwtPayload(String? payload) {
try {
// Payload must be normalized before passing it to the codec
return jwtCodec.decode(base64.normalize(payload!)) as Map<String, Object?>?;
} catch (_) {
// Do nothing, we always return null for any failure.
}
return null;
}
/// Converts a [CredentialResponse] into a [GoogleSignInUserData].
///
/// May return `null`, if the `credentialResponse` is null, or its `credential`
/// cannot be decoded.
GoogleSignInUserData? gisResponsesToUserData(
CredentialResponse? credentialResponse) {
if (credentialResponse == null || credentialResponse.credential == null) {
return null;
}
final Map<String, Object?>? payload =
getJwtTokenPayload(credentialResponse.credential);
if (payload == null) {
return null;
}
return GoogleSignInUserData(
email: payload['email']! as String,
id: payload['sub']! as String,
displayName: payload['name']! as String,
photoUrl: payload['picture']! as String,
idToken: credentialResponse.credential,
);
}
/// Converts responses from the GIS library into TokenData for the plugin.
GoogleSignInTokenData gisResponsesToTokenData(
CredentialResponse? credentialResponse, TokenResponse? tokenResponse) {
return GoogleSignInTokenData(
idToken: credentialResponse?.credential,
accessToken: tokenResponse?.access_token,
);
}