Skip to content

Commit

Permalink
Fix auth_time in Auth Emulator.
Browse files Browse the repository at this point in the history
  • Loading branch information
yuchenshi committed Aug 13, 2021
1 parent b29b3e2 commit 812eff0
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 10 deletions.
39 changes: 30 additions & 9 deletions src/emulator/auth/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1709,9 +1709,9 @@ function generateJwt(
}
}

const customAttributes = JSON.parse(user.customAttributes || "{}");
const customAttributes = JSON.parse(user.customAttributes || "{}") as Record<string, unknown>;
/* eslint-disable camelcase */
const customPayloadFields: FirebaseJwtPayload = {
const customPayloadFields: Partial<FirebaseJwtPayload> = {
// Non-reserved fields (set before custom attributes):
name: user.displayName,
picture: user.photoUrl,
Expand All @@ -1726,12 +1726,7 @@ function generateJwt(
// This field is only set for anonymous sign-in but not for any other
// provider (such as email or Google) in production. Let's match that.
provider_id: signInProvider === "anonymous" ? signInProvider : undefined,
auth_time:
user.lastLoginAt != null
? toUnixTimestamp(new Date(user.lastLoginAt))
: user.lastRefreshAt != null
? toUnixTimestamp(new Date(user.lastRefreshAt))
: toUnixTimestamp(new Date()),
auth_time: toUnixTimestamp(getAuthTime(user)),
user_id: user.localId,
firebase: {
identities,
Expand All @@ -1755,6 +1750,27 @@ function generateJwt(
return jwtStr;
}

function getAuthTime(user: UserInfo): Date {
if (user.lastLoginAt != null) {
const millisSinceEpoch = parseInt(user.lastLoginAt, 10);
const authTime = new Date(millisSinceEpoch);
if (isNaN(authTime.getTime())) {
throw new Error(`Internal assertion error: invalid user.lastLoginAt = ${user.lastLoginAt}`);
}
return authTime;
} else if (user.lastRefreshAt) {
const authTime = new Date(user.lastRefreshAt); // Parse from ISO date string.
if (isNaN(authTime.getTime())) {
throw new Error(
`Internal assertion error: invalid user.lastRefreshAt = ${user.lastRefreshAt}`
);
}
return authTime;
} else {
throw new Error(`Internal assertion error: Missing user.lastLoginAt and user.lastRefreshAt`);
}
}

function verifyPhoneNumber(state: ProjectState, sessionInfo: string, code: string): string {
const verification = state.getVerificationCodeBySessionInfo(sessionInfo);
assert(verification, "INVALID_SESSION_INFO");
Expand Down Expand Up @@ -2127,12 +2143,17 @@ export interface FirebaseJwtPayload {
exp: number; // expiresAt (in seconds since epoch)
iss: string; // issuer
aud: string; // audience (=projectId)
auth_time: number; // lastLoginAt (in seconds since epoch)
// ...and other fields that we don't care for now.

// Firebase-specific fields:

// the last login time (in seconds since epoch), may be different from iat
auth_time: number;
email?: string;
email_verified?: boolean;
phone_number?: string;
name?: string;
picture?: string;
user_id: string;
provider_id?: string;
firebase: {
Expand Down
3 changes: 2 additions & 1 deletion src/test/emulators/auth/misc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describeAuthEmulator("token refresh", ({ authApi, getClock }) => {
});

it("should populate auth_time to match lastLoginAt (in seconds since epoch)", async () => {
getClock().tick(444); // Make timestamps a bit more interesting (non-zero).
const emailUser = { email: "alice@example.com", password: "notasecret" };
const { refreshToken } = await registerUser(authApi(), emailUser);

Expand All @@ -60,7 +61,7 @@ describeAuthEmulator("token refresh", ({ authApi, getClock }) => {
const idToken = res.body.id_token;
const user = await getAccountInfoByIdToken(authApi(), idToken);
expect(user.lastLoginAt).not.to.be.undefined;
const lastLoginAtSeconds = toUnixTimestamp(new Date(user.lastLoginAt!));
const lastLoginAtSeconds = Math.floor(parseInt(user.lastLoginAt!, 10) / 1000);
const decoded = decodeJwt(idToken, { complete: true }) as {
header: JwtHeader;
payload: FirebaseJwtPayload;
Expand Down

0 comments on commit 812eff0

Please sign in to comment.