Skip to content

Commit c5045b5

Browse files
committed
user record changes for getAccountInfo()
1 parent 1ab9273 commit c5045b5

File tree

2 files changed

+157
-1
lines changed

2 files changed

+157
-1
lines changed

src/auth/user-record.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ export interface TotpInfoResponse {
5656
[key: string]: unknown;
5757
}
5858

59+
export interface PasskeyInfoResponse {
60+
name?: string;
61+
credentialId?: string;
62+
displayName?: string;
63+
[key: string]: unknown;
64+
}
65+
5966
export interface ProviderUserInfoResponse {
6067
rawId: string;
6168
displayName?: string;
@@ -81,6 +88,7 @@ export interface GetAccountInfoUserResponse {
8188
tenantId?: string;
8289
providerUserInfo?: ProviderUserInfoResponse[];
8390
mfaInfo?: MultiFactorInfoResponse[];
91+
passkeyInfo?: PasskeyInfoResponse[];
8492
createdAt?: string;
8593
lastLoginAt?: string;
8694
lastRefreshAt?: string;
@@ -357,6 +365,50 @@ export class MultiFactorSettings {
357365
}
358366
}
359367

368+
/**
369+
* Information about passkeys
370+
*/
371+
export class PasskeyInfo {
372+
/**
373+
* The name of the user.
374+
*/
375+
public readonly name?: string;
376+
/**
377+
* Identifier for the registered credential.
378+
*/
379+
public readonly credentialId: string;
380+
/**
381+
* The human-readable name of the user, intended for display.
382+
*/
383+
public readonly displayName?: string;
384+
385+
constructor(response: PasskeyInfoResponse) {
386+
if (!isNonNullObject(response)) {
387+
throw new FirebaseAuthError(
388+
AuthClientErrorCode.INTERNAL_ERROR,
389+
'INTERNAL ASSERT FAILED: Invalid passkey info response');
390+
}
391+
utils.addReadonlyGetter(this, 'name', response.name);
392+
utils.addReadonlyGetter(this, 'credentialId', response.credentialId);
393+
utils.addReadonlyGetter(this, 'displayName', response.displayName);
394+
}
395+
396+
/**
397+
* Returns a JSON-serializable representation of this passkey info object.
398+
*
399+
* @returns A JSON-serializable representation of this passkey info object.
400+
*/
401+
public toJSON(): object {
402+
const json: any = {
403+
name: this.name,
404+
credentialId: this.credentialId,
405+
displayName: this.displayName,
406+
}
407+
return json;
408+
}
409+
410+
}
411+
360412
/**
361413
* Represents a user's metadata.
362414
*/
@@ -582,6 +634,11 @@ export class UserRecord {
582634
*/
583635
public readonly multiFactor?: MultiFactorSettings;
584636

637+
/**
638+
* Information about Passkeys
639+
*/
640+
public readonly passkeyInfo?: PasskeyInfo[];
641+
585642
/**
586643
* @param response - The server side response returned from the getAccountInfo
587644
* endpoint.
@@ -637,6 +694,15 @@ export class UserRecord {
637694
if (multiFactor.enrolledFactors.length > 0) {
638695
utils.addReadonlyGetter(this, 'multiFactor', multiFactor);
639696
}
697+
if(response.passkeyInfo) {
698+
let passkeys: PasskeyInfo[] = [];
699+
response.passkeyInfo.forEach((passkey) => {
700+
passkeys.push(new PasskeyInfo(passkey));
701+
});
702+
if(passkeys.length > 0) {
703+
utils.addReadonlyGetter(this, 'passkeyInfo', passkeys);
704+
}
705+
}
640706
}
641707

642708
/**
@@ -664,6 +730,12 @@ export class UserRecord {
664730
if (this.multiFactor) {
665731
json.multiFactor = this.multiFactor.toJSON();
666732
}
733+
if(this.passkeyInfo) {
734+
json.passkeyInfo = [];
735+
this.passkeyInfo.forEach((passkey) => {
736+
json.passkeyInfo.push(passkey.toJSON());
737+
})
738+
}
667739
json.providerData = [];
668740
for (const entry of this.providerData) {
669741
// Convert each provider data to json.

test/unit/auth/user-record.spec.ts

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import * as chaiAsPromised from 'chai-as-promised';
2121

2222
import { deepCopy } from '../../../src/utils/deep-copy';
2323
import {
24-
GetAccountInfoUserResponse, ProviderUserInfoResponse, MultiFactorInfoResponse, TotpMultiFactorInfo,
24+
GetAccountInfoUserResponse, ProviderUserInfoResponse, MultiFactorInfoResponse, TotpMultiFactorInfo, PasskeyInfo,
2525
} from '../../../src/auth/user-record';
2626
import {
2727
UserInfo, UserMetadata, UserRecord, MultiFactorSettings, MultiFactorInfo, PhoneMultiFactorInfo,
@@ -100,6 +100,18 @@ function getValidUserResponse(tenantId?: string): GetAccountInfoUserResponse {
100100
phoneInfo: '+16505556789',
101101
},
102102
],
103+
passkeyInfo: [
104+
{
105+
106+
credentialId: "credentialId1",
107+
displayName: "passkey1",
108+
},
109+
{
110+
111+
credentialId: "credentialId2",
112+
displayName: "passkey2",
113+
}
114+
]
103115
};
104116
if (typeof tenantId !== 'undefined') {
105117
response.tenantId = tenantId;
@@ -185,6 +197,18 @@ function getUserJSON(tenantId?: string): object {
185197
},
186198
],
187199
},
200+
passkeyInfo: [
201+
{
202+
203+
credentialId: "credentialId1",
204+
displayName: "passkey1",
205+
},
206+
{
207+
208+
credentialId: "credentialId2",
209+
displayName: "passkey2",
210+
}
211+
]
188212
};
189213
}
190214

@@ -663,6 +687,66 @@ describe('MultiFactorSettings', () => {
663687
});
664688
});
665689

690+
describe('PasskeyInfo', () => {
691+
const passkeyInfoData = {
692+
name: 'John Doe',
693+
credentialId: 'credential123',
694+
displayName: '[email protected]',
695+
};
696+
const passkeyInfo = new PasskeyInfo(passkeyInfoData);
697+
698+
describe('constructor', () => {
699+
it('should create a PasskeyInfo object with valid data', () => {
700+
expect(passkeyInfo).to.be.an.instanceOf(PasskeyInfo);
701+
});
702+
703+
it('should throw when missing required fields', () => {
704+
expect(() => {
705+
return new PasskeyInfo({});
706+
}).to.throw('INTERNAL ASSERT FAILED: Invalid passkey info response');
707+
});
708+
});
709+
710+
describe('getters', () => {
711+
it('should return the expected name', () => {
712+
expect(passkeyInfo.name).to.equal(passkeyInfoData.name);
713+
});
714+
715+
it('should throw when modifying readonly name property', () => {
716+
expect(() => {
717+
(passkeyInfo as any).name = 'Modified Name';
718+
}).to.throw(Error);
719+
});
720+
721+
it('should return the expected credentialId', () => {
722+
expect(passkeyInfo.credentialId).to.equal(passkeyInfoData.credentialId);
723+
});
724+
725+
it('should throw when modifying readonly credentialId property', () => {
726+
expect(() => {
727+
(passkeyInfo as any).credentialId = 'modifiedCredential';
728+
}).to.throw(Error);
729+
});
730+
731+
it('should return the expected displayName', () => {
732+
expect(passkeyInfo.displayName).to.equal(passkeyInfoData.displayName);
733+
});
734+
735+
it('should throw when modifying readonly displayName property', () => {
736+
expect(() => {
737+
(passkeyInfo as any).displayName = 'modifiedDisplayName';
738+
}).to.throw(Error);
739+
});
740+
});
741+
742+
describe('toJSON', () => {
743+
it('should return the expected JSON object', () => {
744+
expect(passkeyInfo.toJSON()).to.deep.equal(passkeyInfoData);
745+
});
746+
});
747+
});
748+
749+
666750
describe('UserInfo', () => {
667751
describe('constructor', () => {
668752
it('should throw when an empty object is provided', () => {

0 commit comments

Comments
 (0)