Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ kfurue <pulpits.37-shims@icloud.com>
Max Wojczuk <developer@mwojczuk.xyz>
Julian Pscheid <julian@pscheid.com>
Artem Shevchenko <art.shevchen@gmail.com>
Bilal Aksoy <sloeyeleet@gmail.com>
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ class IdTokenResult extends JsObjectWrapper<auth_interop.IdTokenResultImpl> {
/// Note, this does not map to provider IDs.
String? get signInProvider => jsObject.signInProvider?.toDart;

/// The type of second factor associated with this session, provided the
/// user was multi-factor authenticated (for example, phone or totp).
String? get signInSecondFactor => jsObject.signInSecondFactor?.toDart;

/// The Firebase Auth ID token JWT string.
String get token => jsObject.token.toDart;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ extension type IdTokenResultImpl._(JSObject _) implements JSObject {
external JSString get expirationTime;
external JSString get issuedAtTime;
external JSString? get signInProvider;
external JSString? get signInSecondFactor;
external JSString get token;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ IdTokenResult convertWebIdTokenResult(
expirationTimestamp:
webIdTokenResult.expirationTime.millisecondsSinceEpoch,
signInProvider: webIdTokenResult.signInProvider,
signInSecondFactor: webIdTokenResult.signInSecondFactor,
),
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2026 The Chromium Authors. 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 'dart:js_interop';
import 'dart:js_interop_unsafe';

import 'package:firebase_auth_web/src/interop/auth.dart' as auth_interop;
import 'package:firebase_auth_web/src/interop/auth_interop.dart'
as auth_js_interop;
import 'package:firebase_auth_web/src/utils/web_utils.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
// Shaped like the object the JS SDK's getIdTokenResult() resolves to.
JSObject buildJsIdTokenResult({String? signInSecondFactor}) {
final firebaseClaim = JSObject()..['sign_in_provider'] = 'password'.toJS;
if (signInSecondFactor != null) {
firebaseClaim['sign_in_second_factor'] = signInSecondFactor.toJS;
}
return JSObject()
..['claims'] = (JSObject()..['firebase'] = firebaseClaim)
..['token'] = 'test-token'.toJS
..['authTime'] = 'Fri, 22 Sep 2017 01:49:58 GMT'.toJS
..['issuedAtTime'] = 'Fri, 22 Sep 2017 01:49:58 GMT'.toJS
..['expirationTime'] = 'Fri, 22 Sep 2017 02:49:58 GMT'.toJS
..['signInProvider'] = 'password'.toJS
..['signInSecondFactor'] = signInSecondFactor?.toJS;
}

Future<auth_interop.IdTokenResult> getIdTokenResult(JSObject jsResult) {
JSPromise<JSObject> fakeGetIdTokenResult(JSBoolean? forceRefresh) =>
Future<JSObject>.value(jsResult).toJS;
final jsUser = JSObject()..['getIdTokenResult'] = fakeGetIdTokenResult.toJS;
final user = auth_interop.User.getInstance(
jsUser as auth_js_interop.UserJsImpl,
)!;
return user.getIdTokenResult(false);
Comment on lines +38 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The repository style guide prohibits the use of the ! operator. Please use flow analysis or a null check instead.

    final user = auth_interop.User.getInstance(
      jsUser as auth_js_interop.UserJsImpl,
    );
    if (user == null) {
      throw StateError('User instance should not be null');
    }
    return user.getIdTokenResult(false);
References
  1. Null Safety: NO ! operator. Use ? and flow analysis (e.g. if (x != null)). (link)

}

group('convertWebIdTokenResult', () {
test('passes signInSecondFactor through', () async {
final result = convertWebIdTokenResult(
await getIdTokenResult(
buildJsIdTokenResult(signInSecondFactor: 'totp'),
),
);

expect(result.signInProvider, 'password');
expect(result.signInSecondFactor, 'totp');
});

test('returns null signInSecondFactor without a second factor', () async {
final result = convertWebIdTokenResult(
await getIdTokenResult(buildJsIdTokenResult()),
);

expect(result.signInProvider, 'password');
expect(result.signInSecondFactor, isNull);
});
});
}
Loading