firebase-admin-edge 1.0.18 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +28 -0
  2. package/dist/auth/auth-endpoint-errors.d.ts +15 -0
  3. package/dist/auth/auth-endpoint-errors.d.ts.map +1 -0
  4. package/dist/auth/auth-endpoint-errors.js +120 -0
  5. package/dist/auth/auth-endpoint-errors.js.map +1 -0
  6. package/dist/auth/auth-error-codes.d.ts +777 -0
  7. package/dist/auth/auth-error-codes.d.ts.map +1 -0
  8. package/dist/auth/auth-error-codes.js +823 -0
  9. package/dist/auth/auth-error-codes.js.map +1 -0
  10. package/dist/auth/errors.d.ts +20 -0
  11. package/dist/auth/errors.d.ts.map +1 -0
  12. package/dist/auth/errors.js +29 -0
  13. package/dist/auth/errors.js.map +1 -0
  14. package/dist/auth/firebase-admin-auth.d.ts +10 -20
  15. package/dist/auth/firebase-admin-auth.d.ts.map +1 -1
  16. package/dist/auth/firebase-admin-auth.js +13 -12
  17. package/dist/auth/firebase-admin-auth.js.map +1 -1
  18. package/dist/auth/firebase-admin-auth.test.js +34 -22
  19. package/dist/auth/firebase-admin-auth.test.js.map +1 -1
  20. package/dist/auth/firebase-auth-endpoints.d.ts +9 -72
  21. package/dist/auth/firebase-auth-endpoints.d.ts.map +1 -1
  22. package/dist/auth/firebase-auth-endpoints.js +10 -8
  23. package/dist/auth/firebase-auth-endpoints.js.map +1 -1
  24. package/dist/auth/firebase-auth-endpoints.test.js +13 -4
  25. package/dist/auth/firebase-auth-endpoints.test.js.map +1 -1
  26. package/dist/auth/firebase-auth.d.ts +3 -8
  27. package/dist/auth/firebase-auth.d.ts.map +1 -1
  28. package/dist/auth/firebase-auth.js +58 -20
  29. package/dist/auth/firebase-auth.js.map +1 -1
  30. package/dist/auth/firebase-auth.test.js +26 -9
  31. package/dist/auth/firebase-auth.test.js.map +1 -1
  32. package/dist/auth/firebase-jwt.d.ts +5 -29
  33. package/dist/auth/firebase-jwt.d.ts.map +1 -1
  34. package/dist/auth/firebase-jwt.js +74 -33
  35. package/dist/auth/firebase-jwt.js.map +1 -1
  36. package/dist/auth/firebase-jwt.test.js +31 -11
  37. package/dist/auth/firebase-jwt.test.js.map +1 -1
  38. package/dist/auth/github-oauth.d.ts +5 -2
  39. package/dist/auth/github-oauth.d.ts.map +1 -1
  40. package/dist/auth/github-oauth.js +126 -13
  41. package/dist/auth/github-oauth.js.map +1 -1
  42. package/dist/auth/github-oauth.test.d.ts +2 -0
  43. package/dist/auth/github-oauth.test.d.ts.map +1 -0
  44. package/dist/auth/github-oauth.test.js +202 -0
  45. package/dist/auth/github-oauth.test.js.map +1 -0
  46. package/dist/auth/google-oauth.d.ts +3 -3
  47. package/dist/auth/google-oauth.d.ts.map +1 -1
  48. package/dist/auth/google-oauth.js +89 -26
  49. package/dist/auth/google-oauth.js.map +1 -1
  50. package/dist/auth/google-oauth.test.js +105 -35
  51. package/dist/auth/google-oauth.test.js.map +1 -1
  52. package/dist/auth/oauth.d.ts +3 -0
  53. package/dist/auth/oauth.d.ts.map +1 -0
  54. package/dist/auth/oauth.js +28 -0
  55. package/dist/auth/oauth.js.map +1 -0
  56. package/dist/auth/oauth.test.d.ts +2 -0
  57. package/dist/auth/oauth.test.d.ts.map +1 -0
  58. package/dist/auth/oauth.test.js +233 -0
  59. package/dist/auth/oauth.test.js.map +1 -0
  60. package/dist/firebase-edge-errors.d.ts +95 -0
  61. package/dist/firebase-edge-errors.d.ts.map +1 -0
  62. package/dist/firebase-edge-errors.js +102 -0
  63. package/dist/firebase-edge-errors.js.map +1 -0
  64. package/dist/firebase-edge-errors.test.d.ts +2 -0
  65. package/dist/firebase-edge-errors.test.d.ts.map +1 -0
  66. package/dist/firebase-edge-errors.test.js +72 -0
  67. package/dist/firebase-edge-errors.test.js.map +1 -0
  68. package/dist/firebase-edge-server.d.ts +2 -3
  69. package/dist/firebase-edge-server.d.ts.map +1 -1
  70. package/dist/firebase-edge-server.js +16 -13
  71. package/dist/firebase-edge-server.js.map +1 -1
  72. package/dist/firebase-edge-server.test.js +10 -7
  73. package/dist/firebase-edge-server.test.js.map +1 -1
  74. package/package.json +1 -1
@@ -0,0 +1,202 @@
1
+ import { describe, expect, it, beforeEach, vi } from 'vitest';
2
+ import { exchangeCodeForGitHubIdToken } from './github-oauth.js';
3
+ import { FirebaseEdgeError } from './errors.js';
4
+ import { GitHubErrorInfo } from './auth-error-codes.js';
5
+ const restFetchMock = vi.hoisted(() => vi.fn());
6
+ vi.mock('../rest-fetch.js', () => ({
7
+ restFetch: restFetchMock
8
+ }));
9
+ beforeEach(() => {
10
+ restFetchMock.mockReset();
11
+ });
12
+ describe('GitHub OAuth Token Exchange', () => {
13
+ const payload = {
14
+ code: 'auth-code',
15
+ redirect_uri: 'https://example.com/callback',
16
+ client_id: 'client-123',
17
+ client_secret: 'secret-xyz'
18
+ };
19
+ it('returns token data when REST call succeeds', async () => {
20
+ const expectedData = {
21
+ access_token: 'gho_token',
22
+ token_type: 'bearer',
23
+ scope: 'read:user,user:email'
24
+ };
25
+ restFetchMock.mockResolvedValue({ data: expectedData, error: null });
26
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
27
+ expect(restFetchMock).toHaveBeenCalledWith('https://github.com/login/oauth/access_token', expect.objectContaining({
28
+ body: expect.objectContaining(payload),
29
+ form: true,
30
+ acceptJson: true
31
+ }));
32
+ expect(result).toEqual({ data: expectedData, error: null });
33
+ });
34
+ it('handles incorrect client credentials error', async () => {
35
+ const githubError = {
36
+ error: 'incorrect_client_credentials',
37
+ error_description: 'The client_id and/or client_secret passed are incorrect.'
38
+ };
39
+ restFetchMock.mockResolvedValue({
40
+ data: null,
41
+ error: githubError
42
+ });
43
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
44
+ expect(result.data).toBeNull();
45
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
46
+ expect(result.error?.code).toBe('auth/github-incorrect-client-credentials');
47
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_INCORRECT_CLIENT_CREDENTIALS.message);
48
+ });
49
+ it('handles redirect URI mismatch error', async () => {
50
+ const githubError = {
51
+ error: 'redirect_uri_mismatch',
52
+ error_description: 'The redirect_uri MUST match the registered callback URL for this application.'
53
+ };
54
+ restFetchMock.mockResolvedValue({
55
+ data: null,
56
+ error: githubError
57
+ });
58
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
59
+ expect(result.data).toBeNull();
60
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
61
+ expect(result.error?.code).toBe('auth/github-redirect-uri-mismatch');
62
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_REDIRECT_URI_MISMATCH.message);
63
+ });
64
+ it('handles bad verification code error', async () => {
65
+ const githubError = {
66
+ error: 'bad_verification_code',
67
+ error_description: 'The code passed is incorrect or expired.'
68
+ };
69
+ restFetchMock.mockResolvedValue({
70
+ data: null,
71
+ error: githubError
72
+ });
73
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
74
+ expect(result.data).toBeNull();
75
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
76
+ expect(result.error?.code).toBe('auth/github-bad-verification-code');
77
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_BAD_VERIFICATION_CODE.message);
78
+ });
79
+ it('handles unverified user email error', async () => {
80
+ const githubError = {
81
+ error: 'unverified_user_email',
82
+ error_description: 'The user must have a verified email address.'
83
+ };
84
+ restFetchMock.mockResolvedValue({
85
+ data: null,
86
+ error: githubError
87
+ });
88
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
89
+ expect(result.data).toBeNull();
90
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
91
+ expect(result.error?.code).toBe('auth/github-unverified-user-email');
92
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_UNVERIFIED_USER_EMAIL.message);
93
+ });
94
+ it('handles access denied error', async () => {
95
+ const githubError = {
96
+ error: 'access_denied',
97
+ error_description: 'The user denied the request.'
98
+ };
99
+ restFetchMock.mockResolvedValue({
100
+ data: null,
101
+ error: githubError
102
+ });
103
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
104
+ expect(result.data).toBeNull();
105
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
106
+ expect(result.error?.code).toBe('auth/github-access-denied');
107
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_ACCESS_DENIED.message);
108
+ });
109
+ it('handles invalid client error', async () => {
110
+ const githubError = {
111
+ error: 'invalid_client',
112
+ error_description: 'Client authentication failed.'
113
+ };
114
+ restFetchMock.mockResolvedValue({
115
+ data: null,
116
+ error: githubError
117
+ });
118
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
119
+ expect(result.data).toBeNull();
120
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
121
+ expect(result.error?.code).toBe('auth/github-invalid-client');
122
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_INVALID_CLIENT.message);
123
+ });
124
+ it('handles invalid request error', async () => {
125
+ const githubError = {
126
+ error: 'invalid_request',
127
+ error_description: 'A required parameter was missing or invalid.'
128
+ };
129
+ restFetchMock.mockResolvedValue({
130
+ data: null,
131
+ error: githubError
132
+ });
133
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
134
+ expect(result.data).toBeNull();
135
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
136
+ expect(result.error?.code).toBe('auth/github-invalid-request');
137
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_INVALID_REQUEST.message);
138
+ });
139
+ it('handles unauthorized client error', async () => {
140
+ const githubError = {
141
+ error: 'unauthorized_client',
142
+ error_description: 'The client is not authorized to request an access token.'
143
+ };
144
+ restFetchMock.mockResolvedValue({
145
+ data: null,
146
+ error: githubError
147
+ });
148
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
149
+ expect(result.data).toBeNull();
150
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
151
+ expect(result.error?.code).toBe('auth/github-unauthorized-client');
152
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_UNAUTHORIZED_CLIENT.message);
153
+ });
154
+ it('handles invalid scope error', async () => {
155
+ const githubError = {
156
+ error: 'invalid_scope',
157
+ error_description: 'The requested scope is invalid.'
158
+ };
159
+ restFetchMock.mockResolvedValue({
160
+ data: null,
161
+ error: githubError
162
+ });
163
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
164
+ expect(result.data).toBeNull();
165
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
166
+ expect(result.error?.code).toBe('auth/github-invalid-scope');
167
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_INVALID_SCOPE.message);
168
+ });
169
+ it('handles unrecognized GitHub error', async () => {
170
+ const githubError = {
171
+ error: 'unknown_error',
172
+ error_description: 'Some unknown error occurred.'
173
+ };
174
+ restFetchMock.mockResolvedValue({
175
+ data: null,
176
+ error: githubError
177
+ });
178
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
179
+ expect(result.data).toBeNull();
180
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
181
+ expect(result.error?.code).toBe('auth/github-code-exchange-failed');
182
+ expect(result.error?.message).toBe(GitHubErrorInfo.GITHUB_CODE_EXCHANGE_FAILED.message);
183
+ });
184
+ it('includes error context in FirebaseEdgeError', async () => {
185
+ const githubError = {
186
+ error: 'bad_verification_code',
187
+ error_description: 'The code passed is incorrect or expired.'
188
+ };
189
+ restFetchMock.mockResolvedValue({
190
+ data: null,
191
+ error: githubError
192
+ });
193
+ const result = await exchangeCodeForGitHubIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
194
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
195
+ const firebaseError = result.error;
196
+ expect(firebaseError.context).toEqual({
197
+ originalError: 'bad_verification_code',
198
+ description: 'The code passed is incorrect or expired.'
199
+ });
200
+ });
201
+ });
202
+ //# sourceMappingURL=github-oauth.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-oauth.test.js","sourceRoot":"","sources":["../../src/auth/github-oauth.test.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,15 @@
1
1
  import type { GoogleTokenResponse, ServiceAccount } from './firebase-types.js';
2
- export declare function createGoogleOAuthLoginUrl(redirect_uri: string, path: string, client_id: string): string;
2
+ import { FirebaseEdgeError } from './errors.js';
3
3
  export declare function exchangeCodeForGoogleIdToken(code: string, redirect_uri: string, client_id: string, client_secret: string, fetchFn?: typeof globalThis.fetch): Promise<{
4
4
  data: null;
5
- error: Error;
5
+ error: FirebaseEdgeError;
6
6
  } | {
7
7
  data: GoogleTokenResponse | null;
8
8
  error: null;
9
9
  }>;
10
10
  export declare function getToken(serviceAccount: ServiceAccount, fetch?: typeof globalThis.fetch): Promise<{
11
11
  data: null;
12
- error: Error;
12
+ error: FirebaseEdgeError;
13
13
  } | {
14
14
  data: GoogleTokenResponse | null;
15
15
  error: null;
@@ -1 +1 @@
1
- {"version":3,"file":"google-oauth.d.ts","sourceRoot":"","sources":["../../src/auth/google-oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAER,mBAAmB,EACnB,cAAc,EACjB,MAAM,qBAAqB,CAAC;AAM7B,wBAAgB,yBAAyB,CACrC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,UAiBpB;AAED,wBAAsB,4BAA4B,CAC9C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK;;;;;;GAgCpC;AAED,wBAAsB,QAAQ,CAC1B,cAAc,EAAE,cAAc,EAC9B,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK;;;;;;GA6DlC"}
1
+ {"version":3,"file":"google-oauth.d.ts","sourceRoot":"","sources":["../../src/auth/google-oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAER,mBAAmB,EACnB,cAAc,EACjB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,iBAAiB,EAAe,MAAM,aAAa,CAAC;AAK7D,wBAAsB,4BAA4B,CAC9C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK;;;;;;GA8GpC;AAED,wBAAsB,QAAQ,CAC1B,cAAc,EAAE,cAAc,EAC9B,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK;;;;;;GAyGlC"}
@@ -1,21 +1,8 @@
1
1
  import { restFetch } from '../rest-fetch.js';
2
2
  import { signJWT } from './firebase-jwt.js';
3
+ import { FirebaseEdgeError, ensureError } from './errors.js';
4
+ import { GoogleErrorInfo } from './auth-error-codes.js';
3
5
  // TODO - allow scope customization
4
- export function createGoogleOAuthLoginUrl(redirect_uri, path, client_id) {
5
- return new URL('https://accounts.google.com/o/oauth2/v2/auth?' +
6
- new URLSearchParams({
7
- client_id,
8
- redirect_uri,
9
- response_type: 'code',
10
- scope: 'openid email profile',
11
- access_type: 'offline',
12
- prompt: 'consent',
13
- state: JSON.stringify({
14
- next: path,
15
- provider: 'google'
16
- })
17
- }).toString()).toString();
18
- }
19
6
  export async function exchangeCodeForGoogleIdToken(code, redirect_uri, client_id, client_secret, fetchFn) {
20
7
  const url = 'https://oauth2.googleapis.com/token';
21
8
  const { data, error } = await restFetch(url, {
@@ -30,9 +17,61 @@ export async function exchangeCodeForGoogleIdToken(code, redirect_uri, client_id
30
17
  form: true
31
18
  });
32
19
  if (error?.error.message) {
20
+ const errorMessage = error.error.message.toLowerCase();
21
+ if (errorMessage.includes('invalid_grant')) {
22
+ return {
23
+ data: null,
24
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_INVALID_GRANT, {
25
+ context: { originalError: error.error.message }
26
+ })
27
+ };
28
+ }
29
+ if (errorMessage.includes('invalid_client')) {
30
+ return {
31
+ data: null,
32
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_INVALID_CLIENT, {
33
+ context: { originalError: error.error.message }
34
+ })
35
+ };
36
+ }
37
+ if (errorMessage.includes('invalid_request')) {
38
+ return {
39
+ data: null,
40
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_INVALID_REQUEST, {
41
+ context: { originalError: error.error.message }
42
+ })
43
+ };
44
+ }
45
+ if (errorMessage.includes('access_denied')) {
46
+ return {
47
+ data: null,
48
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_ACCESS_DENIED, {
49
+ context: { originalError: error.error.message }
50
+ })
51
+ };
52
+ }
53
+ if (errorMessage.includes('unsupported_response_type')) {
54
+ return {
55
+ data: null,
56
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_UNSUPPORTED_RESPONSE_TYPE, {
57
+ context: { originalError: error.error.message }
58
+ })
59
+ };
60
+ }
61
+ if (errorMessage.includes('invalid_scope')) {
62
+ return {
63
+ data: null,
64
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_INVALID_SCOPE, {
65
+ context: { originalError: error.error.message }
66
+ })
67
+ };
68
+ }
69
+ // Default case for unrecognized errors
33
70
  return {
34
71
  data: null,
35
- error: new Error(`Failed to exchange code for ID token: ${error.error.message}`)
72
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_CODE_EXCHANGE_FAILED, {
73
+ context: { originalError: error.error.message }
74
+ })
36
75
  };
37
76
  }
38
77
  return {
@@ -47,13 +86,16 @@ export async function getToken(serviceAccount, fetch) {
47
86
  if (jwtError) {
48
87
  return {
49
88
  data: null,
50
- error: new Error(`Failed to sign JWT: ${jwtError.message}`)
89
+ error: new FirebaseEdgeError(GoogleErrorInfo.JWT_SIGN_FAILED, {
90
+ cause: ensureError(jwtError),
91
+ context: { originalError: jwtError.message }
92
+ })
51
93
  };
52
94
  }
53
95
  if (!jwtData) {
54
96
  return {
55
97
  data: null,
56
- error: new Error('No JWT data returned')
98
+ error: new FirebaseEdgeError(GoogleErrorInfo.JWT_DATA_MISSING)
57
99
  };
58
100
  }
59
101
  const { data, error } = await restFetch(url, {
@@ -69,9 +111,31 @@ export async function getToken(serviceAccount, fetch) {
69
111
  form: true
70
112
  });
71
113
  if (error?.error.message) {
114
+ const errorMessage = error.error.message.toLowerCase();
115
+ if (errorMessage.includes('unavailable') ||
116
+ errorMessage.includes('503')) {
117
+ return {
118
+ data: null,
119
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_TEMPORARILY_UNAVAILABLE, {
120
+ context: { originalError: error.error.message }
121
+ })
122
+ };
123
+ }
124
+ if (errorMessage.includes('server') ||
125
+ errorMessage.includes('500')) {
126
+ return {
127
+ data: null,
128
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_SERVER_ERROR, {
129
+ context: { originalError: error.error.message }
130
+ })
131
+ };
132
+ }
133
+ // Default case for other service account token errors
72
134
  return {
73
135
  data: null,
74
- error: new Error(`Failed to get token: ${error.error.message}`)
136
+ error: new FirebaseEdgeError(GoogleErrorInfo.SERVICE_ACCOUNT_TOKEN_FAILED, {
137
+ context: { originalError: error.error.message }
138
+ })
75
139
  };
76
140
  }
77
141
  return {
@@ -80,15 +144,14 @@ export async function getToken(serviceAccount, fetch) {
80
144
  };
81
145
  }
82
146
  catch (e) {
83
- if (e instanceof Error) {
84
- return {
85
- data: null,
86
- error: new Error(`Failed to get token: ${e.message}`)
87
- };
88
- }
89
147
  return {
90
148
  data: null,
91
- error: e
149
+ error: new FirebaseEdgeError(GoogleErrorInfo.GOOGLE_TOKEN_REQUEST_FAILED, {
150
+ cause: ensureError(e),
151
+ context: {
152
+ originalError: e instanceof Error ? e.message : String(e)
153
+ }
154
+ })
92
155
  };
93
156
  }
94
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"google-oauth.js","sourceRoot":"","sources":["../../src/auth/google-oauth.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,mCAAmC;AAEnC,MAAM,UAAU,yBAAyB,CACrC,YAAoB,EACpB,IAAY,EACZ,SAAiB;IAEjB,OAAO,IAAI,GAAG,CACV,+CAA+C;QAC3C,IAAI,eAAe,CAAC;YAChB,SAAS;YACT,YAAY;YACZ,aAAa,EAAE,MAAM;YACrB,KAAK,EAAE,sBAAsB;YAC7B,WAAW,EAAE,SAAS;YACtB,MAAM,EAAE,SAAS;YACjB,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC;gBAClB,IAAI,EAAE,IAAI;gBACV,QAAQ,EAAE,QAAQ;aACrB,CAAC;SACL,CAAC,CAAC,QAAQ,EAAE,CACpB,CAAC,QAAQ,EAAE,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAC9C,IAAY,EACZ,YAAoB,EACpB,SAAiB,EACjB,aAAqB,EACrB,OAAiC;IAEjC,MAAM,GAAG,GAAG,qCAAqC,CAAC;IAElD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,SAAS,CAGrC,GAAG,EAAE;QACH,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;QAC1B,IAAI,EAAE;YACF,IAAI;YACJ,SAAS;YACT,aAAa;YACb,YAAY;YACZ,UAAU,EAAE,oBAAoB;SACnC;QACD,IAAI,EAAE,IAAI;KACb,CAAC,CAAC;IAEH,IAAI,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO;YACH,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,IAAI,KAAK,CACZ,yCAAyC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CACjE;SACJ,CAAC;IACN,CAAC;IAED,OAAO;QACH,IAAI;QACJ,KAAK,EAAE,IAAI;KACd,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC1B,cAA8B,EAC9B,KAA+B;IAE/B,MAAM,GAAG,GAAG,qCAAqC,CAAC;IAElD,IAAI,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GACpC,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;QAElC,IAAI,QAAQ,EAAE,CAAC;YACX,OAAO;gBACH,IAAI,EAAE,IAAI;gBACV,KAAK,EAAE,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,OAAO,EAAE,CAAC;aAC9D,CAAC;QACN,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO;gBACH,IAAI,EAAE,IAAI;gBACV,KAAK,EAAE,IAAI,KAAK,CAAC,sBAAsB,CAAC;aAC3C,CAAC;QACN,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,SAAS,CAGrC,GAAG,EAAE;YACH,MAAM,EAAE,EAAE,KAAK,EAAE;YACjB,IAAI,EAAE;gBACF,UAAU,EAAE,6CAA6C;gBACzD,SAAS,EAAE,OAAO;aACrB;YACD,OAAO,EAAE;gBACL,eAAe,EAAE,UAAU;gBAC3B,IAAI,EAAE,uBAAuB;aAChC;YACD,IAAI,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,IAAI,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YACvB,OAAO;gBACH,IAAI,EAAE,IAAI;gBACV,KAAK,EAAE,IAAI,KAAK,CAAC,wBAAwB,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;aAClE,CAAC;QACN,CAAC;QAED,OAAO;YACH,IAAI;YACJ,KAAK,EAAE,IAAI;SACd,CAAC;IACN,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACT,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;YACrB,OAAO;gBACH,IAAI,EAAE,IAAI;gBACV,KAAK,EAAE,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,OAAO,EAAE,CAAC;aACxD,CAAC;QACN,CAAC;QACD,OAAO;YACH,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,CAAU;SACpB,CAAC;IACN,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"google-oauth.js","sourceRoot":"","sources":["../../src/auth/google-oauth.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  import { describe, expect, it, beforeEach, vi } from 'vitest';
2
- import { createGoogleOAuthLoginUrl, exchangeCodeForGoogleIdToken, getToken } from './google-oauth.js';
2
+ import { exchangeCodeForGoogleIdToken, getToken } from './google-oauth.js';
3
+ import { FirebaseEdgeError } from './errors.js';
4
+ import { GoogleErrorInfo } from './auth-error-codes.js';
3
5
  const restFetchMock = vi.hoisted(() => vi.fn());
4
6
  const signJWTMock = vi.hoisted(() => vi.fn());
5
7
  vi.mock('../rest-fetch.js', () => ({
@@ -12,24 +14,7 @@ beforeEach(() => {
12
14
  restFetchMock.mockReset();
13
15
  signJWTMock.mockReset();
14
16
  });
15
- describe('createGoogleOAuthLoginUrl', () => {
16
- it('builds expected consent screen URL', () => {
17
- const url = createGoogleOAuthLoginUrl('https://example.com/callback', '/next/path', 'client-123');
18
- const parsed = new URL(url);
19
- expect(parsed.origin + parsed.pathname).toBe('https://accounts.google.com/o/oauth2/v2/auth');
20
- expect(parsed.searchParams.get('client_id')).toBe('client-123');
21
- expect(parsed.searchParams.get('redirect_uri')).toBe('https://example.com/callback');
22
- expect(parsed.searchParams.get('response_type')).toBe('code');
23
- expect(parsed.searchParams.get('scope')).toBe('openid email profile');
24
- expect(parsed.searchParams.get('access_type')).toBe('offline');
25
- expect(parsed.searchParams.get('prompt')).toBe('consent');
26
- expect(JSON.parse(parsed.searchParams.get('state') ?? '')).toEqual({
27
- next: '/next/path',
28
- provider: 'google'
29
- });
30
- });
31
- });
32
- describe('exchangeCodeForGoogleIdToken', () => {
17
+ describe('Google OAuth Token Exchange', () => {
33
18
  const payload = {
34
19
  code: 'auth-code',
35
20
  redirect_uri: 'https://example.com/callback',
@@ -53,9 +38,70 @@ describe('exchangeCodeForGoogleIdToken', () => {
53
38
  error: { error: apiError }
54
39
  });
55
40
  const result = await exchangeCodeForGoogleIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
56
- expect(result).toEqual({
41
+ expect(result.data).toBeNull();
42
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
43
+ expect(result.error?.code).toBe('auth/google-invalid-grant');
44
+ expect(result.error?.message).toBe(GoogleErrorInfo.GOOGLE_INVALID_GRANT.message);
45
+ });
46
+ it('handles invalid client error', async () => {
47
+ const apiError = { code: 401, message: 'invalid_client' };
48
+ restFetchMock.mockResolvedValue({
57
49
  data: null,
58
- error: new Error('Failed to exchange code for ID token: invalid_grant')
50
+ error: { error: apiError }
51
+ });
52
+ const result = await exchangeCodeForGoogleIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
53
+ expect(result.data).toBeNull();
54
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
55
+ expect(result.error?.code).toBe('auth/google-invalid-client');
56
+ expect(result.error?.message).toBe(GoogleErrorInfo.GOOGLE_INVALID_CLIENT.message);
57
+ });
58
+ it('handles invalid request error', async () => {
59
+ const apiError = { code: 400, message: 'invalid_request' };
60
+ restFetchMock.mockResolvedValue({
61
+ data: null,
62
+ error: { error: apiError }
63
+ });
64
+ const result = await exchangeCodeForGoogleIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
65
+ expect(result.data).toBeNull();
66
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
67
+ expect(result.error?.code).toBe('auth/google-invalid-request');
68
+ expect(result.error?.message).toBe(GoogleErrorInfo.GOOGLE_INVALID_REQUEST.message);
69
+ });
70
+ it('handles access denied error', async () => {
71
+ const apiError = { code: 403, message: 'access_denied' };
72
+ restFetchMock.mockResolvedValue({
73
+ data: null,
74
+ error: { error: apiError }
75
+ });
76
+ const result = await exchangeCodeForGoogleIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
77
+ expect(result.data).toBeNull();
78
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
79
+ expect(result.error?.code).toBe('auth/google-access-denied');
80
+ expect(result.error?.message).toBe(GoogleErrorInfo.GOOGLE_ACCESS_DENIED.message);
81
+ });
82
+ it('handles unrecognized error with default fallback', async () => {
83
+ const apiError = { code: 500, message: 'unknown_error' };
84
+ restFetchMock.mockResolvedValue({
85
+ data: null,
86
+ error: { error: apiError }
87
+ });
88
+ const result = await exchangeCodeForGoogleIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
89
+ expect(result.data).toBeNull();
90
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
91
+ expect(result.error?.code).toBe('auth/google-code-exchange-failed');
92
+ expect(result.error?.message).toBe(GoogleErrorInfo.GOOGLE_CODE_EXCHANGE_FAILED.message);
93
+ });
94
+ it('includes error context in FirebaseEdgeError', async () => {
95
+ const apiError = { code: 400, message: 'invalid_grant' };
96
+ restFetchMock.mockResolvedValue({
97
+ data: null,
98
+ error: { error: apiError }
99
+ });
100
+ const result = await exchangeCodeForGoogleIdToken(payload.code, payload.redirect_uri, payload.client_id, payload.client_secret);
101
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
102
+ const firebaseError = result.error;
103
+ expect(firebaseError.context).toEqual({
104
+ originalError: 'invalid_grant'
59
105
  });
60
106
  });
61
107
  });
@@ -91,18 +137,18 @@ describe('getToken', () => {
91
137
  signJWTMock.mockResolvedValue({ data: null, error: jwtError });
92
138
  const result = await getToken(serviceAccount);
93
139
  expect(restFetchMock).not.toHaveBeenCalled();
94
- expect(result).toEqual({
95
- data: null,
96
- error: new Error('Failed to sign JWT: bad cert')
97
- });
140
+ expect(result.data).toBeNull();
141
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
142
+ expect(result.error?.code).toBe('auth/jwt-sign-failed');
143
+ expect(result.error?.message).toBe(GoogleErrorInfo.JWT_SIGN_FAILED.message);
98
144
  });
99
145
  it('reports missing JWT data', async () => {
100
146
  signJWTMock.mockResolvedValue({ data: null, error: null });
101
147
  const result = await getToken(serviceAccount);
102
- expect(result).toEqual({
103
- data: null,
104
- error: new Error('No JWT data returned')
105
- });
148
+ expect(result.data).toBeNull();
149
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
150
+ expect(result.error?.code).toBe('auth/jwt-data-missing');
151
+ expect(result.error?.message).toBe(GoogleErrorInfo.JWT_DATA_MISSING.message);
106
152
  });
107
153
  it('returns REST error payload from Google', async () => {
108
154
  const apiError = { code: 503, message: 'unavailable' };
@@ -112,19 +158,43 @@ describe('getToken', () => {
112
158
  error: { error: apiError }
113
159
  });
114
160
  const result = await getToken(serviceAccount);
115
- expect(result).toEqual({
116
- data: null,
117
- error: new Error('Failed to get token: unavailable')
118
- });
161
+ expect(result.data).toBeNull();
162
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
163
+ expect(result.error?.code).toBe('auth/google-temporarily-unavailable');
164
+ expect(result.error?.message).toBe(GoogleErrorInfo.GOOGLE_TEMPORARILY_UNAVAILABLE.message);
119
165
  });
120
166
  it('handles unexpected exceptions', async () => {
121
167
  signJWTMock.mockResolvedValue({ data: 'signed-jwt', error: null });
122
168
  restFetchMock.mockRejectedValue(new Error('boom'));
123
169
  const result = await getToken(serviceAccount);
124
- expect(result).toEqual({
170
+ expect(result.data).toBeNull();
171
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
172
+ expect(result.error?.code).toBe('auth/google-token-request-failed');
173
+ expect(result.error?.message).toBe(GoogleErrorInfo.GOOGLE_TOKEN_REQUEST_FAILED.message);
174
+ });
175
+ it('handles server error with appropriate mapping', async () => {
176
+ const apiError = { code: 500, message: 'Internal server error' };
177
+ signJWTMock.mockResolvedValue({ data: 'signed-jwt', error: null });
178
+ restFetchMock.mockResolvedValue({
125
179
  data: null,
126
- error: new Error('Failed to get token: boom')
180
+ error: { error: apiError }
181
+ });
182
+ const result = await getToken(serviceAccount);
183
+ expect(result.data).toBeNull();
184
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
185
+ expect(result.error?.code).toBe('auth/google-server-error');
186
+ expect(result.error?.message).toBe(GoogleErrorInfo.GOOGLE_SERVER_ERROR.message);
187
+ });
188
+ it('includes error context in FirebaseEdgeError', async () => {
189
+ const jwtError = { code: 401, message: 'bad cert', errors: [] };
190
+ signJWTMock.mockResolvedValue({ data: null, error: jwtError });
191
+ const result = await getToken(serviceAccount);
192
+ expect(result.error).toBeInstanceOf(FirebaseEdgeError);
193
+ const firebaseError = result.error;
194
+ expect(firebaseError.context).toEqual({
195
+ originalError: 'bad cert'
127
196
  });
197
+ expect(firebaseError.cause).toBeInstanceOf(Error);
128
198
  });
129
199
  });
130
200
  //# sourceMappingURL=google-oauth.test.js.map