Skip to content

Commit af2c516

Browse files
authored
Move common auth items into common path (firebase#1026)
* moving common auth items to identity.ts and fixing UserRecord definitions * fixing copyright year and exporting old artifacts
1 parent 297b15b commit af2c516

File tree

4 files changed

+215
-155
lines changed

4 files changed

+215
-155
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2022 Firebase
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
22+
23+
import { expect } from 'chai';
24+
import * as identity from '../../../src/common/providers/identity';
25+
26+
describe('identity', () => {
27+
describe('userRecordConstructor', () => {
28+
it('will provide falsey values for fields that are not in raw wire data', () => {
29+
const record = identity.userRecordConstructor({ uid: '123' });
30+
expect(record.toJSON()).to.deep.equal({
31+
uid: '123',
32+
email: null,
33+
emailVerified: false,
34+
displayName: null,
35+
photoURL: null,
36+
phoneNumber: null,
37+
disabled: false,
38+
providerData: [],
39+
customClaims: {},
40+
passwordSalt: null,
41+
passwordHash: null,
42+
tokensValidAfterTime: null,
43+
metadata: {
44+
creationTime: null,
45+
lastSignInTime: null,
46+
},
47+
});
48+
});
49+
50+
it('will not interfere with fields that are in raw wire data', () => {
51+
const raw: any = {
52+
uid: '123',
53+
54+
emailVerified: true,
55+
displayName: 'User',
56+
photoURL: 'url',
57+
phoneNumber: '1233332222',
58+
disabled: true,
59+
providerData: [],
60+
customClaims: {},
61+
passwordSalt: 'abc',
62+
passwordHash: 'def',
63+
tokensValidAfterTime: '2027-02-02T23:01:19.797Z',
64+
metadata: {
65+
creationTime: '2017-02-02T23:06:26.124Z',
66+
lastSignInTime: '2017-02-02T23:01:19.797Z',
67+
},
68+
};
69+
const record = identity.userRecordConstructor(raw);
70+
expect(record.toJSON()).to.deep.equal(raw);
71+
});
72+
73+
it('will convert raw wire fields createdAt and lastSignedInAt to creationTime and lastSignInTime', () => {
74+
const raw: any = {
75+
uid: '123',
76+
metadata: {
77+
createdAt: '2017-02-02T23:06:26.124Z',
78+
lastSignedInAt: '2017-02-02T23:01:19.797Z',
79+
},
80+
};
81+
const record = identity.userRecordConstructor(raw);
82+
expect(record.metadata).to.deep.equal({
83+
creationTime: '2017-02-02T23:06:26.124Z',
84+
lastSignInTime: '2017-02-02T23:01:19.797Z',
85+
});
86+
});
87+
});
88+
});

spec/v1/providers/auth.spec.ts

Lines changed: 6 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@
2121
// SOFTWARE.
2222

2323
import { expect } from 'chai';
24-
import * as firebase from 'firebase-admin';
25-
2624
import {
2725
CloudFunction,
2826
Event,
2927
EventContext,
3028
} from '../../../src/cloud-functions';
29+
import { UserRecord } from '../../../src/common/providers/identity';
3130
import * as functions from '../../../src/index';
3231
import * as auth from '../../../src/providers/auth';
3332

@@ -75,7 +74,7 @@ describe('Auth Functions', () => {
7574
};
7675
}
7776

78-
const handler = (user: firebase.auth.UserRecord) => {
77+
const handler = (user: UserRecord) => {
7978
return Promise.resolve();
8079
};
8180

@@ -137,14 +136,12 @@ describe('Auth Functions', () => {
137136
});
138137

139138
describe('#_dataConstructor', () => {
140-
let cloudFunctionDelete: CloudFunction<firebase.auth.UserRecord>;
139+
let cloudFunctionDelete: CloudFunction<UserRecord>;
141140

142141
before(() => {
143142
cloudFunctionDelete = auth
144143
.user()
145-
.onDelete(
146-
(data: firebase.auth.UserRecord, context: EventContext) => data
147-
);
144+
.onDelete((data: UserRecord, context: EventContext) => data);
148145
});
149146

150147
it('should handle wire format as of v5.0.0 of firebase-admin', () => {
@@ -162,68 +159,6 @@ describe('Auth Functions', () => {
162159
});
163160
});
164161

165-
describe('userRecordConstructor', () => {
166-
it('will provide falsey values for fields that are not in raw wire data', () => {
167-
const record = auth.userRecordConstructor({ uid: '123' });
168-
expect(record.toJSON()).to.deep.equal({
169-
uid: '123',
170-
email: null,
171-
emailVerified: false,
172-
displayName: null,
173-
photoURL: null,
174-
phoneNumber: null,
175-
disabled: false,
176-
providerData: [],
177-
customClaims: {},
178-
passwordSalt: null,
179-
passwordHash: null,
180-
tokensValidAfterTime: null,
181-
metadata: {
182-
creationTime: null,
183-
lastSignInTime: null,
184-
},
185-
});
186-
});
187-
188-
it('will not interfere with fields that are in raw wire data', () => {
189-
const raw: any = {
190-
uid: '123',
191-
192-
emailVerified: true,
193-
displayName: 'User',
194-
photoURL: 'url',
195-
phoneNumber: '1233332222',
196-
disabled: true,
197-
providerData: [],
198-
customClaims: {},
199-
passwordSalt: 'abc',
200-
passwordHash: 'def',
201-
tokensValidAfterTime: '2027-02-02T23:01:19.797Z',
202-
metadata: {
203-
creationTime: '2017-02-02T23:06:26.124Z',
204-
lastSignInTime: '2017-02-02T23:01:19.797Z',
205-
},
206-
};
207-
const record = auth.userRecordConstructor(raw);
208-
expect(record.toJSON()).to.deep.equal(raw);
209-
});
210-
211-
it('will convert raw wire fields createdAt and lastSignedInAt to creationTime and lastSignInTime', () => {
212-
const raw: any = {
213-
uid: '123',
214-
metadata: {
215-
createdAt: '2017-02-02T23:06:26.124Z',
216-
lastSignedInAt: '2017-02-02T23:01:19.797Z',
217-
},
218-
};
219-
const record = auth.userRecordConstructor(raw);
220-
expect(record.metadata).to.deep.equal({
221-
creationTime: '2017-02-02T23:06:26.124Z',
222-
lastSignInTime: '2017-02-02T23:01:19.797Z',
223-
});
224-
});
225-
});
226-
227162
describe('handler namespace', () => {
228163
describe('#onCreate', () => {
229164
it('should return an empty trigger', () => {
@@ -238,8 +173,8 @@ describe('Auth Functions', () => {
238173
});
239174

240175
describe('#onDelete', () => {
241-
const cloudFunctionDelete: CloudFunction<firebase.auth.UserRecord> = functions.handler.auth.user.onDelete(
242-
(data: firebase.auth.UserRecord) => data
176+
const cloudFunctionDelete: CloudFunction<UserRecord> = functions.handler.auth.user.onDelete(
177+
(data: UserRecord) => data
243178
);
244179

245180
it('should return an empty trigger', () => {

src/common/providers/identity.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2022 Firebase
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
22+
23+
import * as firebase from 'firebase-admin';
24+
import * as _ from 'lodash';
25+
26+
/**
27+
* The UserRecord passed to Cloud Functions is the same UserRecord that is returned by the Firebase Admin
28+
* SDK.
29+
*/
30+
export type UserRecord = firebase.auth.UserRecord;
31+
32+
/**
33+
* UserInfo that is part of the UserRecord
34+
*/
35+
export type UserInfo = firebase.auth.UserInfo;
36+
37+
/**
38+
* Helper class to create the user metadata in a UserRecord object
39+
*/
40+
export class UserRecordMetadata implements firebase.auth.UserMetadata {
41+
constructor(public creationTime: string, public lastSignInTime: string) {}
42+
43+
/** Returns a plain JavaScript object with the properties of UserRecordMetadata. */
44+
toJSON() {
45+
return {
46+
creationTime: this.creationTime,
47+
lastSignInTime: this.lastSignInTime,
48+
};
49+
}
50+
}
51+
52+
/**
53+
* Helper function that creates a UserRecord Class from data sent over the wire.
54+
* @param wireData data sent over the wire
55+
* @returns an instance of UserRecord with correct toJSON functions
56+
*/
57+
export function userRecordConstructor(wireData: Object): UserRecord {
58+
// Falsey values from the wire format proto get lost when converted to JSON, this adds them back.
59+
const falseyValues: any = {
60+
email: null,
61+
emailVerified: false,
62+
displayName: null,
63+
photoURL: null,
64+
phoneNumber: null,
65+
disabled: false,
66+
providerData: [],
67+
customClaims: {},
68+
passwordSalt: null,
69+
passwordHash: null,
70+
tokensValidAfterTime: null,
71+
};
72+
const record = _.assign({}, falseyValues, wireData);
73+
74+
const meta = _.get(record, 'metadata');
75+
if (meta) {
76+
_.set(
77+
record,
78+
'metadata',
79+
new UserRecordMetadata(
80+
meta.createdAt || meta.creationTime,
81+
meta.lastSignedInAt || meta.lastSignInTime
82+
)
83+
);
84+
} else {
85+
_.set(record, 'metadata', new UserRecordMetadata(null, null));
86+
}
87+
_.forEach(record.providerData, (entry) => {
88+
_.set(entry, 'toJSON', () => {
89+
return entry;
90+
});
91+
});
92+
_.set(record, 'toJSON', () => {
93+
const json: any = _.pick(record, [
94+
'uid',
95+
'email',
96+
'emailVerified',
97+
'displayName',
98+
'photoURL',
99+
'phoneNumber',
100+
'disabled',
101+
'passwordHash',
102+
'passwordSalt',
103+
'tokensValidAfterTime',
104+
]);
105+
json.metadata = _.get(record, 'metadata').toJSON();
106+
json.customClaims = _.cloneDeep(record.customClaims);
107+
json.providerData = _.map(record.providerData, (entry) => entry.toJSON());
108+
return json;
109+
});
110+
return record as UserRecord;
111+
}

0 commit comments

Comments
 (0)