Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
5 changes: 5 additions & 0 deletions src/frontend/src/lib/flows/authFlow.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
decodeJWT,
extractIssuerTemplateClaims,
} from "$lib/utils/openID";
import { nanosToMillis } from "$lib/utils/time";

interface AuthFlowOptions {
trackLastUsed?: boolean;
Expand Down Expand Up @@ -111,6 +112,7 @@ export class AuthFlow {
identityNumber,
name: info.name[0],
authMethod: { passkey: { credentialId } },
createdAtMillis: info.created_at.map(nanosToMillis)[0],
});
}
return identityNumber;
Expand Down Expand Up @@ -211,6 +213,7 @@ export class AuthFlow {
authMethod: {
openid: { iss, sub, metadata: authnMethod?.metadata, loginHint },
},
createdAtMillis: info.created_at.map(nanosToMillis)[0],
});
}
return { identityNumber, type: "signIn" };
Expand Down Expand Up @@ -313,6 +316,7 @@ export class AuthFlow {
identityNumber,
name: passkeyIdentity.getName(),
authMethod: { passkey: { credentialId } },
createdAtMillis: Date.now(),
});
}
this.#captcha = undefined;
Expand Down Expand Up @@ -425,6 +429,7 @@ export class AuthFlow {
identityNumber,
name,
authMethod: { openid: { iss, sub, loginHint, metadata } },
createdAtMillis: Date.now(),
});
}
this.#captcha = undefined;
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/src/lib/flows/migrationFlow.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ export class MigrationFlow {
credentialId: new Uint8Array(credentialId),
},
},
// Legacy identities do not have the `created_at` property
createdAtMillis: undefined,
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DiscoverableDummyIdentity } from "$lib/utils/discoverableDummyIdentity"
import { DiscoverablePasskeyIdentity } from "$lib/utils/discoverablePasskeyIdentity";
import { inferPasskeyAlias, loadUAParser } from "$lib/legacy/flows/register";
import { lastUsedIdentitiesStore } from "$lib/stores/last-used-identities.store";
import { nanosToMillis } from "$lib/utils/time";

const POLL_INTERVAL = 3000; // Should be frequent enough

Expand All @@ -20,6 +21,7 @@ export class RegisterAccessMethodFlow {
>("continueFromExistingDevice");
#confirmationCode = $state<string>();
#identityName = $state<string>();
#createdAtMillis = $state<number>();
#identityNumber = $state<bigint>();
#existingDeviceLink = $state<URL>();

Expand Down Expand Up @@ -79,6 +81,7 @@ export class RegisterAccessMethodFlow {
// Show confirm sign-in view if session has been confirmed
if (nonNullish(info)) {
this.#identityName = info.name[0] ?? identityNumber.toString(10);
this.#createdAtMillis = info.created_at.map(nanosToMillis)[0];
this.#view = "confirmSignIn";
return;
}
Expand Down Expand Up @@ -134,6 +137,7 @@ export class RegisterAccessMethodFlow {
credentialId: new Uint8Array(credentialId),
},
},
createdAtMillis: this.#createdAtMillis,
});
return this.#identityNumber;
};
Expand Down
5 changes: 4 additions & 1 deletion src/frontend/src/lib/generated/internet_identity_idl.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,10 @@ export const idlFactory = ({ IDL }) => {
const AuthnMethodSecuritySettingsReplaceError = IDL.Variant({
'AuthnMethodNotFound' : IDL.Null,
});
const AuthnMethodSessionInfo = IDL.Record({ 'name' : IDL.Opt(IDL.Text) });
const AuthnMethodSessionInfo = IDL.Record({
'name' : IDL.Opt(IDL.Text),
'created_at' : IDL.Opt(Timestamp),
});
const CheckCaptchaArg = IDL.Record({ 'solution' : IDL.Text });
const RegistrationFlowNextStep = IDL.Variant({
'CheckCaptcha' : IDL.Record({ 'captcha_png_base64' : IDL.Text }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@ export type AuthnMethodSecuritySettingsReplaceError = {
*/
'AuthnMethodNotFound' : null
};
export interface AuthnMethodSessionInfo { 'name' : [] | [string] }
export interface AuthnMethodSessionInfo {
'name' : [] | [string],
'created_at' : [] | [Timestamp],
}
export interface BufferedArchiveEntry {
'sequence_number' : bigint,
'entry' : Uint8Array | number[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ describe("lastUsedIdentitiesStore", () => {
});

it("should add the first identity correctly", () => {
const createdAtMillis = 1690000000000;
lastUsedIdentitiesStore.addLastUsedIdentity({
identityNumber: identity1,
name: name1,
authMethod: { passkey: { credentialId: credId1 } },
createdAtMillis,
});

const expected: LastUsedIdentities = {
Expand All @@ -59,6 +61,7 @@ describe("lastUsedIdentitiesStore", () => {
name: name1,
authMethod: { passkey: { credentialId: credId1 } },
lastUsedTimestampMillis: mockTimestamp1,
createdAtMillis,
},
};
expect(get(lastUsedIdentitiesStore).identities).toEqual(expected);
Expand Down Expand Up @@ -176,17 +179,20 @@ describe("lastUsedIdentityStore (derived store)", () => {
});

it("should return the only identity when one is added", () => {
const createdAtMillis = 1690000000000;
lastUsedIdentitiesStore.addLastUsedIdentity({
identityNumber: identity1,
name: name1,
authMethod: { passkey: { credentialId: credId1 } },
createdAtMillis,
});

const expected: LastUsedIdentity = {
identityNumber: identity1,
name: name1,
authMethod: { passkey: { credentialId: credId1 } },
lastUsedTimestampMillis: mockTimestamp1,
createdAtMillis,
};
expect(get(lastUsedIdentityStore)).toEqual(expected);
});
Expand Down
6 changes: 5 additions & 1 deletion src/frontend/src/lib/stores/last-used-identities.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type LastUsedIdentity = {
};
accounts?: LastUsedAccounts;
lastUsedTimestampMillis: number;
createdAtMillis?: number;
};
export type LastUsedIdentities = {
[identityNumber: string]: LastUsedIdentity;
Expand All @@ -43,7 +44,10 @@ type LastUsedIdentitiesStore = Readable<{
selected?: LastUsedIdentity;
}> & {
addLastUsedIdentity: (
params: Pick<LastUsedIdentity, "identityNumber" | "name" | "authMethod">,
params: Pick<
LastUsedIdentity,
"identityNumber" | "name" | "authMethod" | "createdAtMillis"
>,
) => void;
addLastUsedAccount: (
params: Omit<LastUsedAccount, "lastUsedTimestampMillis">,
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/src/lib/utils/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ export const formatLastUsage = (timestamp: Date): string => {

return relativeTime;
};

export const nanosToMillis = (nanos: bigint): number =>
Number(nanos / BigInt(1_000_000));
1 change: 1 addition & 0 deletions src/internet_identity/internet_identity.did
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ type AuthnMethodConfirmationCode = record {

type AuthnMethodSessionInfo = record {
name : opt text;
created_at : opt Timestamp;
};

type RegistrationId = text;
Expand Down
1 change: 1 addition & 0 deletions src/internet_identity/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,7 @@ mod v2_api {
.is_some_and(|confirmed_session| confirmed_session == caller())
.then_some(AuthnMethodSessionInfo {
name: state::anchor(identity_number).name(),
created_at: state::anchor(identity_number).created_at(),
Comment thread
sea-snake marked this conversation as resolved.
Outdated
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,5 @@ pub enum LookupByRegistrationIdError {
#[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)]
pub struct AuthnMethodSessionInfo {
pub name: Option<String>,
pub created_at: Option<Timestamp>,
}
Loading