firebase-admin-edge 1.1.1 → 3.0.0
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.
- package/README.md +103 -260
- package/dist/app-check/app-check-endpoints.d.ts +31 -0
- package/dist/app-check/app-check-endpoints.d.ts.map +1 -0
- package/dist/app-check/app-check-endpoints.js +75 -0
- package/dist/app-check/app-check-endpoints.js.map +1 -0
- package/dist/app-check/app-check-jwt.d.ts +16 -0
- package/dist/app-check/app-check-jwt.d.ts.map +1 -0
- package/dist/app-check/app-check-jwt.js +59 -0
- package/dist/app-check/app-check-jwt.js.map +1 -0
- package/dist/app-check/app-check.d.ts +48 -0
- package/dist/app-check/app-check.d.ts.map +1 -0
- package/dist/app-check/app-check.js +142 -0
- package/dist/app-check/app-check.js.map +1 -0
- package/dist/auth/auth-blocking-types.d.ts +44 -0
- package/dist/auth/auth-blocking-types.d.ts.map +1 -0
- package/dist/auth/auth-blocking-types.js +2 -0
- package/dist/auth/auth-blocking-types.js.map +1 -0
- package/dist/auth/auth-config-types.d.ts +146 -0
- package/dist/auth/auth-config-types.d.ts.map +1 -0
- package/dist/auth/auth-config-types.js +2 -0
- package/dist/auth/auth-config-types.js.map +1 -0
- package/dist/auth/auth-config.d.ts +15 -0
- package/dist/auth/auth-config.d.ts.map +1 -0
- package/dist/auth/auth-config.js +413 -0
- package/dist/auth/auth-config.js.map +1 -0
- package/dist/auth/auth-emulator.d.ts +7 -0
- package/dist/auth/auth-emulator.d.ts.map +1 -0
- package/dist/auth/auth-emulator.js +24 -0
- package/dist/auth/auth-emulator.js.map +1 -0
- package/dist/auth/auth-endpoint-errors.d.ts +2 -0
- package/dist/auth/auth-endpoint-errors.d.ts.map +1 -1
- package/dist/auth/auth-endpoint-errors.js +20 -1
- package/dist/auth/auth-endpoint-errors.js.map +1 -1
- package/dist/auth/email-link.d.ts +24 -0
- package/dist/auth/email-link.d.ts.map +1 -0
- package/dist/auth/email-link.js +103 -0
- package/dist/auth/email-link.js.map +1 -0
- package/dist/auth/firebase-admin-auth.d.ts +56 -8
- package/dist/auth/firebase-admin-auth.d.ts.map +1 -1
- package/dist/auth/firebase-admin-auth.js +323 -63
- package/dist/auth/firebase-admin-auth.js.map +1 -1
- package/dist/auth/firebase-auth-endpoints.d.ts +93 -5
- package/dist/auth/firebase-auth-endpoints.d.ts.map +1 -1
- package/dist/auth/firebase-auth-endpoints.js +342 -73
- package/dist/auth/firebase-auth-endpoints.js.map +1 -1
- package/dist/auth/firebase-auth.d.ts +82 -6
- package/dist/auth/firebase-auth.d.ts.map +1 -1
- package/dist/auth/firebase-auth.js +103 -10
- package/dist/auth/firebase-auth.js.map +1 -1
- package/dist/auth/firebase-jwt.d.ts +19 -10
- package/dist/auth/firebase-jwt.d.ts.map +1 -1
- package/dist/auth/firebase-jwt.js +176 -39
- package/dist/auth/firebase-jwt.js.map +1 -1
- package/dist/auth/firebase-types.d.ts +3 -0
- package/dist/auth/firebase-types.d.ts.map +1 -1
- package/dist/auth/google-oauth.d.ts +0 -1
- package/dist/auth/google-oauth.d.ts.map +1 -1
- package/dist/auth/google-oauth.js +16 -86
- package/dist/auth/google-oauth.js.map +1 -1
- package/dist/auth/identity-builder-types.d.ts +36 -0
- package/dist/auth/identity-builder-types.d.ts.map +1 -0
- package/dist/auth/identity-builder-types.js +2 -0
- package/dist/auth/identity-builder-types.js.map +1 -0
- package/dist/auth/identity-count-query.d.ts +17 -0
- package/dist/auth/identity-count-query.d.ts.map +1 -0
- package/dist/auth/identity-count-query.js +51 -0
- package/dist/auth/identity-count-query.js.map +1 -0
- package/dist/auth/identity-filter.d.ts +9 -0
- package/dist/auth/identity-filter.d.ts.map +1 -0
- package/dist/auth/identity-filter.js +129 -0
- package/dist/auth/identity-filter.js.map +1 -0
- package/dist/auth/identity-query.d.ts +11 -0
- package/dist/auth/identity-query.d.ts.map +1 -0
- package/dist/auth/identity-query.js +122 -0
- package/dist/auth/identity-query.js.map +1 -0
- package/dist/auth/identity-reference.d.ts +131 -0
- package/dist/auth/identity-reference.d.ts.map +1 -0
- package/dist/auth/identity-reference.js +293 -0
- package/dist/auth/identity-reference.js.map +1 -0
- package/dist/auth/identity-types.d.ts +44 -0
- package/dist/auth/identity-types.d.ts.map +1 -0
- package/dist/auth/identity-types.js +2 -0
- package/dist/auth/identity-types.js.map +1 -0
- package/dist/auth/identity-write.d.ts +41 -0
- package/dist/auth/identity-write.d.ts.map +1 -0
- package/dist/auth/identity-write.js +146 -0
- package/dist/auth/identity-write.js.map +1 -0
- package/dist/auth/identity.d.ts +84 -0
- package/dist/auth/identity.d.ts.map +1 -0
- package/dist/auth/identity.js +319 -0
- package/dist/auth/identity.js.map +1 -0
- package/dist/auth/project-config-manager.d.ts +10 -0
- package/dist/auth/project-config-manager.d.ts.map +1 -0
- package/dist/auth/project-config-manager.js +22 -0
- package/dist/auth/project-config-manager.js.map +1 -0
- package/dist/auth/provider-credential.d.ts +43 -0
- package/dist/auth/provider-credential.d.ts.map +1 -0
- package/dist/auth/provider-credential.js +107 -0
- package/dist/auth/provider-credential.js.map +1 -0
- package/dist/auth/provider-session.d.ts +10 -0
- package/dist/auth/provider-session.d.ts.map +1 -0
- package/dist/auth/provider-session.js +28 -0
- package/dist/auth/provider-session.js.map +1 -0
- package/dist/auth/tenant-manager.d.ts +17 -0
- package/dist/auth/tenant-manager.d.ts.map +1 -0
- package/dist/auth/tenant-manager.js +59 -0
- package/dist/auth/tenant-manager.js.map +1 -0
- package/dist/auth/user-import.d.ts +7 -1
- package/dist/auth/user-import.d.ts.map +1 -1
- package/dist/auth/user-import.js +53 -15
- package/dist/auth/user-import.js.map +1 -1
- package/dist/auth/user-record.d.ts +1 -0
- package/dist/auth/user-record.d.ts.map +1 -1
- package/dist/auth/user-record.js +5 -0
- package/dist/auth/user-record.js.map +1 -1
- package/dist/auth/user-request.d.ts +4 -1
- package/dist/auth/user-request.d.ts.map +1 -1
- package/dist/auth/user-request.js +7 -4
- package/dist/auth/user-request.js.map +1 -1
- package/dist/db/aggregate.d.ts +49 -0
- package/dist/db/aggregate.d.ts.map +1 -0
- package/dist/db/aggregate.js +115 -0
- package/dist/db/aggregate.js.map +1 -0
- package/dist/db/bulk-writer.d.ts +55 -0
- package/dist/db/bulk-writer.d.ts.map +1 -0
- package/dist/db/bulk-writer.js +257 -0
- package/dist/db/bulk-writer.js.map +1 -0
- package/dist/db/bundle-builder.d.ts +17 -0
- package/dist/db/bundle-builder.d.ts.map +1 -0
- package/dist/db/bundle-builder.js +118 -0
- package/dist/db/bundle-builder.js.map +1 -0
- package/dist/db/bytes.d.ts +12 -0
- package/dist/db/bytes.d.ts.map +1 -0
- package/dist/db/bytes.js +49 -0
- package/dist/db/bytes.js.map +1 -0
- package/dist/db/collection-group.d.ts +14 -0
- package/dist/db/collection-group.d.ts.map +1 -0
- package/dist/db/collection-group.js +43 -0
- package/dist/db/collection-group.js.map +1 -0
- package/dist/db/collection-reference.d.ts +21 -0
- package/dist/db/collection-reference.d.ts.map +1 -0
- package/dist/db/collection-reference.js +46 -0
- package/dist/db/collection-reference.js.map +1 -0
- package/dist/db/document-reference.d.ts +41 -0
- package/dist/db/document-reference.d.ts.map +1 -0
- package/dist/db/document-reference.js +125 -0
- package/dist/db/document-reference.js.map +1 -0
- package/dist/db/document-snapshot.d.ts +31 -0
- package/dist/db/document-snapshot.d.ts.map +1 -0
- package/dist/db/document-snapshot.js +103 -0
- package/dist/db/document-snapshot.js.map +1 -0
- package/dist/db/explain.d.ts +28 -0
- package/dist/db/explain.d.ts.map +1 -0
- package/dist/db/explain.js +61 -0
- package/dist/db/explain.js.map +1 -0
- package/dist/db/field-path.d.ts +10 -0
- package/dist/db/field-path.d.ts.map +1 -0
- package/dist/db/field-path.js +65 -0
- package/dist/db/field-path.js.map +1 -0
- package/dist/db/field-value.d.ts +17 -0
- package/dist/db/field-value.d.ts.map +1 -0
- package/dist/db/field-value.js +57 -0
- package/dist/db/field-value.js.map +1 -0
- package/dist/db/filter.d.ts +20 -0
- package/dist/db/filter.d.ts.map +1 -0
- package/dist/db/filter.js +48 -0
- package/dist/db/filter.js.map +1 -0
- package/dist/db/firestore-converter.d.ts +11 -0
- package/dist/db/firestore-converter.d.ts.map +1 -0
- package/dist/db/firestore-converter.js +14 -0
- package/dist/db/firestore-converter.js.map +1 -0
- package/dist/db/firestore-document.d.ts +62 -0
- package/dist/db/firestore-document.d.ts.map +1 -0
- package/dist/db/firestore-document.js +238 -0
- package/dist/db/firestore-document.js.map +1 -0
- package/dist/db/firestore-endpoints.d.ts +41 -0
- package/dist/db/firestore-endpoints.d.ts.map +1 -0
- package/dist/db/firestore-endpoints.js +735 -0
- package/dist/db/firestore-endpoints.js.map +1 -0
- package/dist/db/firestore-error-codes.d.ts +24 -0
- package/dist/db/firestore-error-codes.d.ts.map +1 -0
- package/dist/db/firestore-error-codes.js +24 -0
- package/dist/db/firestore-error-codes.js.map +1 -0
- package/dist/db/firestore-logging.d.ts +23 -0
- package/dist/db/firestore-logging.d.ts.map +1 -0
- package/dist/db/firestore-logging.js +41 -0
- package/dist/db/firestore-logging.js.map +1 -0
- package/dist/db/firestore-path.d.ts +4 -0
- package/dist/db/firestore-path.d.ts.map +1 -0
- package/dist/db/firestore-path.js +37 -0
- package/dist/db/firestore-path.js.map +1 -0
- package/dist/db/firestore-results.d.ts +12 -0
- package/dist/db/firestore-results.d.ts.map +1 -0
- package/dist/db/firestore-results.js +19 -0
- package/dist/db/firestore-results.js.map +1 -0
- package/dist/db/firestore-settings.d.ts +22 -0
- package/dist/db/firestore-settings.d.ts.map +1 -0
- package/dist/db/firestore-settings.js +83 -0
- package/dist/db/firestore-settings.js.map +1 -0
- package/dist/db/firestore-telemetry.d.ts +21 -0
- package/dist/db/firestore-telemetry.d.ts.map +1 -0
- package/dist/db/firestore-telemetry.js +84 -0
- package/dist/db/firestore-telemetry.js.map +1 -0
- package/dist/db/firestore-types.d.ts +25 -0
- package/dist/db/firestore-types.d.ts.map +1 -0
- package/dist/db/firestore-types.js +2 -0
- package/dist/db/firestore-types.js.map +1 -0
- package/dist/db/firestore.d.ts +157 -0
- package/dist/db/firestore.d.ts.map +1 -0
- package/dist/db/firestore.js +513 -0
- package/dist/db/firestore.js.map +1 -0
- package/dist/db/geo-point.d.ts +11 -0
- package/dist/db/geo-point.d.ts.map +1 -0
- package/dist/db/geo-point.js +29 -0
- package/dist/db/geo-point.js.map +1 -0
- package/dist/db/json-stream.d.ts +3 -0
- package/dist/db/json-stream.d.ts.map +1 -0
- package/dist/db/json-stream.js +117 -0
- package/dist/db/json-stream.js.map +1 -0
- package/dist/db/pipeline-expression.d.ts +293 -0
- package/dist/db/pipeline-expression.d.ts.map +1 -0
- package/dist/db/pipeline-expression.js +1436 -0
- package/dist/db/pipeline-expression.js.map +1 -0
- package/dist/db/pipeline-query.d.ts +6 -0
- package/dist/db/pipeline-query.d.ts.map +1 -0
- package/dist/db/pipeline-query.js +91 -0
- package/dist/db/pipeline-query.js.map +1 -0
- package/dist/db/pipeline.d.ts +120 -0
- package/dist/db/pipeline.d.ts.map +1 -0
- package/dist/db/pipeline.js +396 -0
- package/dist/db/pipeline.js.map +1 -0
- package/dist/db/query-document-snapshot.d.ts +2 -0
- package/dist/db/query-document-snapshot.d.ts.map +1 -0
- package/dist/db/query-document-snapshot.js +2 -0
- package/dist/db/query-document-snapshot.js.map +1 -0
- package/dist/db/query-partition.d.ts +14 -0
- package/dist/db/query-partition.d.ts.map +1 -0
- package/dist/db/query-partition.js +32 -0
- package/dist/db/query-partition.js.map +1 -0
- package/dist/db/query-request.d.ts +96 -0
- package/dist/db/query-request.d.ts.map +1 -0
- package/dist/db/query-request.js +283 -0
- package/dist/db/query-request.js.map +1 -0
- package/dist/db/query.d.ts +129 -0
- package/dist/db/query.d.ts.map +1 -0
- package/dist/db/query.js +468 -0
- package/dist/db/query.js.map +1 -0
- package/dist/db/recursive-delete.d.ts +6 -0
- package/dist/db/recursive-delete.d.ts.map +1 -0
- package/dist/db/recursive-delete.js +38 -0
- package/dist/db/recursive-delete.js.map +1 -0
- package/dist/db/snapshot-listener.d.ts +10 -0
- package/dist/db/snapshot-listener.d.ts.map +1 -0
- package/dist/db/snapshot-listener.js +71 -0
- package/dist/db/snapshot-listener.js.map +1 -0
- package/dist/db/timestamp.d.ts +29 -0
- package/dist/db/timestamp.d.ts.map +1 -0
- package/dist/db/timestamp.js +98 -0
- package/dist/db/timestamp.js.map +1 -0
- package/dist/db/transaction.d.ts +44 -0
- package/dist/db/transaction.d.ts.map +1 -0
- package/dist/db/transaction.js +177 -0
- package/dist/db/transaction.js.map +1 -0
- package/dist/db/value-equality.d.ts +3 -0
- package/dist/db/value-equality.d.ts.map +1 -0
- package/dist/db/value-equality.js +29 -0
- package/dist/db/value-equality.js.map +1 -0
- package/dist/db/vector-query.d.ts +38 -0
- package/dist/db/vector-query.d.ts.map +1 -0
- package/dist/db/vector-query.js +110 -0
- package/dist/db/vector-query.js.map +1 -0
- package/dist/db/vector-value.d.ts +8 -0
- package/dist/db/vector-value.d.ts.map +1 -0
- package/dist/db/vector-value.js +25 -0
- package/dist/db/vector-value.js.map +1 -0
- package/dist/db/write-batch.d.ts +25 -0
- package/dist/db/write-batch.d.ts.map +1 -0
- package/dist/db/write-batch.js +72 -0
- package/dist/db/write-batch.js.map +1 -0
- package/dist/db/write-request.d.ts +36 -0
- package/dist/db/write-request.d.ts.map +1 -0
- package/dist/db/write-request.js +257 -0
- package/dist/db/write-request.js.map +1 -0
- package/dist/firebase-edge-server.d.ts +145 -36
- package/dist/firebase-edge-server.d.ts.map +1 -1
- package/dist/firebase-edge-server.js +495 -258
- package/dist/firebase-edge-server.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/rest-fetch.d.ts +1 -1
- package/dist/rest-fetch.d.ts.map +1 -1
- package/dist/storage/storage-acl.d.ts +48 -0
- package/dist/storage/storage-acl.d.ts.map +1 -0
- package/dist/storage/storage-acl.js +101 -0
- package/dist/storage/storage-acl.js.map +1 -0
- package/dist/storage/storage-bucket-endpoints.d.ts +10 -0
- package/dist/storage/storage-bucket-endpoints.d.ts.map +1 -0
- package/dist/storage/storage-bucket-endpoints.js +514 -0
- package/dist/storage/storage-bucket-endpoints.js.map +1 -0
- package/dist/storage/storage-bucket-types.d.ts +183 -0
- package/dist/storage/storage-bucket-types.d.ts.map +1 -0
- package/dist/storage/storage-bucket-types.js +2 -0
- package/dist/storage/storage-bucket-types.js.map +1 -0
- package/dist/storage/storage-bucket.d.ts +122 -0
- package/dist/storage/storage-bucket.d.ts.map +1 -0
- package/dist/storage/storage-bucket.js +544 -0
- package/dist/storage/storage-bucket.js.map +1 -0
- package/dist/storage/storage-channel.d.ts +10 -0
- package/dist/storage/storage-channel.d.ts.map +1 -0
- package/dist/storage/storage-channel.js +15 -0
- package/dist/storage/storage-channel.js.map +1 -0
- package/dist/storage/storage-checksum.d.ts +35 -0
- package/dist/storage/storage-checksum.d.ts.map +1 -0
- package/dist/storage/storage-checksum.js +312 -0
- package/dist/storage/storage-checksum.js.map +1 -0
- package/dist/storage/storage-endpoints.d.ts +14 -0
- package/dist/storage/storage-endpoints.d.ts.map +1 -0
- package/dist/storage/storage-endpoints.js +721 -0
- package/dist/storage/storage-endpoints.js.map +1 -0
- package/dist/storage/storage-file.d.ts +126 -0
- package/dist/storage/storage-file.d.ts.map +1 -0
- package/dist/storage/storage-file.js +604 -0
- package/dist/storage/storage-file.js.map +1 -0
- package/dist/storage/storage-hmac-key.d.ts +26 -0
- package/dist/storage/storage-hmac-key.d.ts.map +1 -0
- package/dist/storage/storage-hmac-key.js +45 -0
- package/dist/storage/storage-hmac-key.js.map +1 -0
- package/dist/storage/storage-iam.d.ts +20 -0
- package/dist/storage/storage-iam.d.ts.map +1 -0
- package/dist/storage/storage-iam.js +43 -0
- package/dist/storage/storage-iam.js.map +1 -0
- package/dist/storage/storage-md5.d.ts +11 -0
- package/dist/storage/storage-md5.d.ts.map +1 -0
- package/dist/storage/storage-md5.js +96 -0
- package/dist/storage/storage-md5.js.map +1 -0
- package/dist/storage/storage-notification.d.ts +24 -0
- package/dist/storage/storage-notification.d.ts.map +1 -0
- package/dist/storage/storage-notification.js +40 -0
- package/dist/storage/storage-notification.js.map +1 -0
- package/dist/storage/storage-progress.d.ts +4 -0
- package/dist/storage/storage-progress.d.ts.map +1 -0
- package/dist/storage/storage-progress.js +17 -0
- package/dist/storage/storage-progress.js.map +1 -0
- package/dist/storage/storage-reference-endpoints.d.ts +63 -0
- package/dist/storage/storage-reference-endpoints.d.ts.map +1 -0
- package/dist/storage/storage-reference-endpoints.js +276 -0
- package/dist/storage/storage-reference-endpoints.js.map +1 -0
- package/dist/storage/storage-reference-helpers.d.ts +12 -0
- package/dist/storage/storage-reference-helpers.d.ts.map +1 -0
- package/dist/storage/storage-reference-helpers.js +72 -0
- package/dist/storage/storage-reference-helpers.js.map +1 -0
- package/dist/storage/storage-reference-signing.d.ts +40 -0
- package/dist/storage/storage-reference-signing.d.ts.map +1 -0
- package/dist/storage/storage-reference-signing.js +289 -0
- package/dist/storage/storage-reference-signing.js.map +1 -0
- package/dist/storage/storage-reference-types.d.ts +76 -0
- package/dist/storage/storage-reference-types.d.ts.map +1 -0
- package/dist/storage/storage-reference-types.js +2 -0
- package/dist/storage/storage-reference-types.js.map +1 -0
- package/dist/storage/storage-results.d.ts +8 -0
- package/dist/storage/storage-results.d.ts.map +1 -0
- package/dist/storage/storage-results.js +32 -0
- package/dist/storage/storage-results.js.map +1 -0
- package/dist/storage/storage-resumable.d.ts +18 -0
- package/dist/storage/storage-resumable.d.ts.map +1 -0
- package/dist/storage/storage-resumable.js +166 -0
- package/dist/storage/storage-resumable.js.map +1 -0
- package/dist/storage/storage-retry.d.ts +9 -0
- package/dist/storage/storage-retry.d.ts.map +1 -0
- package/dist/storage/storage-retry.js +104 -0
- package/dist/storage/storage-retry.js.map +1 -0
- package/dist/storage/storage-signed-url.d.ts +9 -0
- package/dist/storage/storage-signed-url.d.ts.map +1 -0
- package/dist/storage/storage-signed-url.js +96 -0
- package/dist/storage/storage-signed-url.js.map +1 -0
- package/dist/storage/storage-special-endpoints.d.ts +10 -0
- package/dist/storage/storage-special-endpoints.d.ts.map +1 -0
- package/dist/storage/storage-special-endpoints.js +394 -0
- package/dist/storage/storage-special-endpoints.js.map +1 -0
- package/dist/storage/storage-special-types.d.ts +154 -0
- package/dist/storage/storage-special-types.d.ts.map +1 -0
- package/dist/storage/storage-special-types.js +2 -0
- package/dist/storage/storage-special-types.js.map +1 -0
- package/dist/storage/storage-types.d.ts +251 -0
- package/dist/storage/storage-types.d.ts.map +1 -0
- package/dist/storage/storage-types.js +2 -0
- package/dist/storage/storage-types.js.map +1 -0
- package/dist/storage/storage-upload-stream.d.ts +41 -0
- package/dist/storage/storage-upload-stream.d.ts.map +1 -0
- package/dist/storage/storage-upload-stream.js +335 -0
- package/dist/storage/storage-upload-stream.js.map +1 -0
- package/dist/storage/storage-xml-signing.d.ts +17 -0
- package/dist/storage/storage-xml-signing.d.ts.map +1 -0
- package/dist/storage/storage-xml-signing.js +144 -0
- package/dist/storage/storage-xml-signing.js.map +1 -0
- package/dist/storage/storage.d.ts +159 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.js +545 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/utils/token-cache.d.ts.map +1 -1
- package/dist/utils/token-cache.js +3 -2
- package/dist/utils/token-cache.js.map +1 -1
- package/package.json +24 -8
- package/dist/auth/auth-endpoint-errors.test.d.ts +0 -2
- package/dist/auth/auth-endpoint-errors.test.d.ts.map +0 -1
- package/dist/auth/auth-endpoint-errors.test.js +0 -411
- package/dist/auth/auth-endpoint-errors.test.js.map +0 -1
- package/dist/auth/email-action-request.test.d.ts +0 -2
- package/dist/auth/email-action-request.test.d.ts.map +0 -1
- package/dist/auth/email-action-request.test.js +0 -128
- package/dist/auth/email-action-request.test.js.map +0 -1
- package/dist/auth/errors.test.d.ts +0 -2
- package/dist/auth/errors.test.d.ts.map +0 -1
- package/dist/auth/errors.test.js +0 -123
- package/dist/auth/errors.test.js.map +0 -1
- package/dist/auth/firebase-admin-auth.test.d.ts +0 -2
- package/dist/auth/firebase-admin-auth.test.d.ts.map +0 -1
- package/dist/auth/firebase-admin-auth.test.js +0 -1542
- package/dist/auth/firebase-admin-auth.test.js.map +0 -1
- package/dist/auth/firebase-auth-endpoints.test.d.ts +0 -2
- package/dist/auth/firebase-auth-endpoints.test.d.ts.map +0 -1
- package/dist/auth/firebase-auth-endpoints.test.js +0 -1082
- package/dist/auth/firebase-auth-endpoints.test.js.map +0 -1
- package/dist/auth/firebase-auth.test.d.ts +0 -2
- package/dist/auth/firebase-auth.test.d.ts.map +0 -1
- package/dist/auth/firebase-auth.test.js +0 -167
- package/dist/auth/firebase-auth.test.js.map +0 -1
- package/dist/auth/firebase-jwt.test.d.ts +0 -2
- package/dist/auth/firebase-jwt.test.d.ts.map +0 -1
- package/dist/auth/firebase-jwt.test.js +0 -164
- package/dist/auth/firebase-jwt.test.js.map +0 -1
- package/dist/auth/github-oauth.d.ts +0 -19
- package/dist/auth/github-oauth.d.ts.map +0 -1
- package/dist/auth/github-oauth.js +0 -145
- package/dist/auth/github-oauth.js.map +0 -1
- package/dist/auth/github-oauth.test.d.ts +0 -2
- package/dist/auth/github-oauth.test.d.ts.map +0 -1
- package/dist/auth/github-oauth.test.js +0 -202
- package/dist/auth/github-oauth.test.js.map +0 -1
- package/dist/auth/google-oauth.test.d.ts +0 -2
- package/dist/auth/google-oauth.test.d.ts.map +0 -1
- package/dist/auth/google-oauth.test.js +0 -200
- package/dist/auth/google-oauth.test.js.map +0 -1
- package/dist/auth/oauth.d.ts +0 -3
- package/dist/auth/oauth.d.ts.map +0 -1
- package/dist/auth/oauth.js +0 -48
- package/dist/auth/oauth.js.map +0 -1
- package/dist/auth/oauth.test.d.ts +0 -2
- package/dist/auth/oauth.test.d.ts.map +0 -1
- package/dist/auth/oauth.test.js +0 -109
- package/dist/auth/oauth.test.js.map +0 -1
- package/dist/auth/user-batch.test.d.ts +0 -2
- package/dist/auth/user-batch.test.d.ts.map +0 -1
- package/dist/auth/user-batch.test.js +0 -73
- package/dist/auth/user-batch.test.js.map +0 -1
- package/dist/auth/user-import.test.d.ts +0 -2
- package/dist/auth/user-import.test.d.ts.map +0 -1
- package/dist/auth/user-import.test.js +0 -217
- package/dist/auth/user-import.test.js.map +0 -1
- package/dist/auth/user-record.test.d.ts +0 -2
- package/dist/auth/user-record.test.d.ts.map +0 -1
- package/dist/auth/user-record.test.js +0 -198
- package/dist/auth/user-record.test.js.map +0 -1
- package/dist/auth/user-request.test.d.ts +0 -2
- package/dist/auth/user-request.test.d.ts.map +0 -1
- package/dist/auth/user-request.test.js +0 -327
- package/dist/auth/user-request.test.js.map +0 -1
- package/dist/firebase-edge-errors.test.d.ts +0 -2
- package/dist/firebase-edge-errors.test.d.ts.map +0 -1
- package/dist/firebase-edge-errors.test.js +0 -72
- package/dist/firebase-edge-errors.test.js.map +0 -1
- package/dist/firebase-edge-server.test.d.ts +0 -2
- package/dist/firebase-edge-server.test.d.ts.map +0 -1
- package/dist/firebase-edge-server.test.js +0 -293
- package/dist/firebase-edge-server.test.js.map +0 -1
- package/dist/rest-fetch.test.d.ts +0 -2
- package/dist/rest-fetch.test.d.ts.map +0 -1
- package/dist/rest-fetch.test.js +0 -164
- package/dist/rest-fetch.test.js.map +0 -1
|
@@ -1,1542 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { FirebaseAdminAuth } from './firebase-admin-auth.js';
|
|
3
|
-
import { generateEmailActionLink, deleteAccountsAdmin, importAccountsAdmin, getAccountsInfo, createAccountAdmin, updateAccountAdmin, deleteAccountAdmin, downloadAccount, getAccountInfo, createSessionCookie as createSessionCookieEndpoint } from './firebase-auth-endpoints.js';
|
|
4
|
-
import { getToken } from './google-oauth.js';
|
|
5
|
-
import { signJWTCustomToken, verifyJWT, verifySessionJWT } from './firebase-jwt.js';
|
|
6
|
-
import { FirebaseEdgeError, FirebaseAdminAuthErrorInfo, FirebaseEndpointErrorInfo, JWTErrorInfo } from './errors.js';
|
|
7
|
-
vi.mock('./firebase-auth-endpoints.js', () => ({
|
|
8
|
-
generateEmailActionLink: vi.fn(),
|
|
9
|
-
deleteAccountsAdmin: vi.fn(),
|
|
10
|
-
importAccountsAdmin: vi.fn(),
|
|
11
|
-
getAccountsInfo: vi.fn(),
|
|
12
|
-
createAccountAdmin: vi.fn(),
|
|
13
|
-
updateAccountAdmin: vi.fn(),
|
|
14
|
-
deleteAccountAdmin: vi.fn(),
|
|
15
|
-
downloadAccount: vi.fn(),
|
|
16
|
-
getAccountInfo: vi.fn(),
|
|
17
|
-
createSessionCookie: vi.fn()
|
|
18
|
-
}));
|
|
19
|
-
vi.mock('./firebase-jwt.js', () => ({
|
|
20
|
-
signJWTCustomToken: vi.fn(),
|
|
21
|
-
verifyJWT: vi.fn(),
|
|
22
|
-
verifySessionJWT: vi.fn()
|
|
23
|
-
}));
|
|
24
|
-
vi.mock('./google-oauth.js', () => ({
|
|
25
|
-
getToken: vi.fn()
|
|
26
|
-
}));
|
|
27
|
-
const mockedGetToken = vi.mocked(getToken);
|
|
28
|
-
const mockedGetAccountInfo = vi.mocked(getAccountInfo);
|
|
29
|
-
const mockedCreateSessionCookieEndpoint = vi.mocked(createSessionCookieEndpoint);
|
|
30
|
-
const mockedVerifyJWT = vi.mocked(verifyJWT);
|
|
31
|
-
const mockedVerifySessionJWT = vi.mocked(verifySessionJWT);
|
|
32
|
-
const mockedSignJWTCustomToken = vi.mocked(signJWTCustomToken);
|
|
33
|
-
const serviceAccountKey = {
|
|
34
|
-
type: 'service_account',
|
|
35
|
-
project_id: 'test-project',
|
|
36
|
-
private_key_id: 'test-private-key-id',
|
|
37
|
-
private_key: 'test-private-key',
|
|
38
|
-
client_email: 'test@test-project.iam.gserviceaccount.com',
|
|
39
|
-
client_id: 'test-client-id',
|
|
40
|
-
auth_uri: 'https://accounts.google.com/o/oauth2/auth',
|
|
41
|
-
token_uri: 'https://oauth2.googleapis.com/token',
|
|
42
|
-
auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs',
|
|
43
|
-
client_x509_cert_url: 'https://www.googleapis.com/robot/v1/metadata/x509/test%40test-project.iam.gserviceaccount.com'
|
|
44
|
-
};
|
|
45
|
-
// Proper mock data that matches the expected types
|
|
46
|
-
const mockGoogleTokenResponse = {
|
|
47
|
-
access_token: 'test-access-token',
|
|
48
|
-
expires_in: 3600,
|
|
49
|
-
scope: 'scope',
|
|
50
|
-
token_type: 'Bearer',
|
|
51
|
-
id_token: 'test-id-token'
|
|
52
|
-
};
|
|
53
|
-
const mockUserRecord = {
|
|
54
|
-
localId: 'uid-1',
|
|
55
|
-
email: 'test@example.com',
|
|
56
|
-
emailVerified: true,
|
|
57
|
-
disabled: false
|
|
58
|
-
};
|
|
59
|
-
const mockFirebasePayload = {
|
|
60
|
-
iss: 'https://securetoken.google.com/test-project',
|
|
61
|
-
aud: 'test-project',
|
|
62
|
-
auth_time: 1000,
|
|
63
|
-
user_id: 'uid-1',
|
|
64
|
-
sub: 'uid-1',
|
|
65
|
-
iat: 1000,
|
|
66
|
-
exp: 2000,
|
|
67
|
-
email: 'test@example.com',
|
|
68
|
-
email_verified: true,
|
|
69
|
-
firebase: {
|
|
70
|
-
identities: {
|
|
71
|
-
email: ['test@example.com']
|
|
72
|
-
},
|
|
73
|
-
sign_in_provider: 'password'
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
describe('FirebaseAdminAuth', () => {
|
|
77
|
-
let auth;
|
|
78
|
-
let authWithTenant;
|
|
79
|
-
beforeEach(() => {
|
|
80
|
-
auth = new FirebaseAdminAuth(serviceAccountKey);
|
|
81
|
-
authWithTenant = new FirebaseAdminAuth(serviceAccountKey, 'test-tenant-id');
|
|
82
|
-
vi.clearAllMocks();
|
|
83
|
-
});
|
|
84
|
-
afterEach(() => {
|
|
85
|
-
vi.resetAllMocks();
|
|
86
|
-
});
|
|
87
|
-
describe('getUserByProviderUid', () => {
|
|
88
|
-
it.each([
|
|
89
|
-
[
|
|
90
|
-
'google.com',
|
|
91
|
-
'google-123',
|
|
92
|
-
{
|
|
93
|
-
federatedUserId: [
|
|
94
|
-
{ providerId: 'google.com', rawId: 'google-123' }
|
|
95
|
-
]
|
|
96
|
-
}
|
|
97
|
-
],
|
|
98
|
-
['email', 'test@example.com', { email: ['test@example.com'] }],
|
|
99
|
-
['phone', '+15551234567', { phoneNumber: ['+15551234567'] }]
|
|
100
|
-
])('looks up %s using the existing batch helper', async (providerId, uid, request) => {
|
|
101
|
-
const fetchFn = vi.fn();
|
|
102
|
-
const admin = new FirebaseAdminAuth(serviceAccountKey, 'tenant', fetchFn);
|
|
103
|
-
mockedGetToken.mockResolvedValue({
|
|
104
|
-
data: mockGoogleTokenResponse,
|
|
105
|
-
error: null
|
|
106
|
-
});
|
|
107
|
-
vi.mocked(getAccountsInfo).mockResolvedValue({
|
|
108
|
-
data: {
|
|
109
|
-
users: [
|
|
110
|
-
{
|
|
111
|
-
...mockUserRecord,
|
|
112
|
-
phoneNumber: '+15551234567',
|
|
113
|
-
providerUserInfo: [
|
|
114
|
-
{
|
|
115
|
-
providerId: 'google.com',
|
|
116
|
-
rawId: 'google-123'
|
|
117
|
-
}
|
|
118
|
-
]
|
|
119
|
-
}
|
|
120
|
-
]
|
|
121
|
-
},
|
|
122
|
-
error: null
|
|
123
|
-
});
|
|
124
|
-
const result = await admin.getUserByProviderUid(providerId, uid);
|
|
125
|
-
expect(result.error).toBeNull();
|
|
126
|
-
expect(result.data?.uid).toBe('uid-1');
|
|
127
|
-
expect(result.data?.toJSON()).toMatchObject({ uid: 'uid-1' });
|
|
128
|
-
expect(getAccountsInfo).toHaveBeenCalledWith(request, 'test-access-token', 'test-project', 'tenant', fetchFn);
|
|
129
|
-
});
|
|
130
|
-
it('returns user-not-found for an unmatched provider UID', async () => {
|
|
131
|
-
vi.spyOn(auth, 'getUsers').mockResolvedValue({
|
|
132
|
-
data: {
|
|
133
|
-
users: [],
|
|
134
|
-
notFound: [
|
|
135
|
-
{ providerId: 'google.com', providerUid: 'missing' }
|
|
136
|
-
]
|
|
137
|
-
},
|
|
138
|
-
error: null
|
|
139
|
-
});
|
|
140
|
-
expect((await auth.getUserByProviderUid('google.com', 'missing')).error
|
|
141
|
-
?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_USER_NOT_FOUND.code);
|
|
142
|
-
});
|
|
143
|
-
it.each([
|
|
144
|
-
['', 'uid'],
|
|
145
|
-
['google.com', ''],
|
|
146
|
-
[null, 'uid'],
|
|
147
|
-
['google.com', null],
|
|
148
|
-
['email', 'invalid'],
|
|
149
|
-
['phone', 'invalid']
|
|
150
|
-
])('rejects invalid identifiers %s/%s before authentication', async (providerId, uid) => {
|
|
151
|
-
expect((await auth.getUserByProviderUid(providerId, uid)).error).toBeInstanceOf(FirebaseEdgeError);
|
|
152
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
153
|
-
});
|
|
154
|
-
it('preserves lookup failures', async () => {
|
|
155
|
-
const error = new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_USER_LOOKUP_FAILED);
|
|
156
|
-
vi.spyOn(auth, 'getUsers').mockResolvedValue({ data: null, error });
|
|
157
|
-
expect(await auth.getUserByProviderUid('google.com', 'uid')).toEqual({ data: null, error });
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
describe('generateVerifyAndChangeEmailLink', () => {
|
|
161
|
-
it.each([undefined, { url: 'https://example.com/account' }])('generates a link with settings %j', async (settings) => {
|
|
162
|
-
const fetchFn = vi.fn();
|
|
163
|
-
const admin = new FirebaseAdminAuth(serviceAccountKey, 'tenant', fetchFn);
|
|
164
|
-
mockedGetToken.mockResolvedValue({
|
|
165
|
-
data: mockGoogleTokenResponse,
|
|
166
|
-
error: null
|
|
167
|
-
});
|
|
168
|
-
vi.mocked(generateEmailActionLink).mockResolvedValue({
|
|
169
|
-
data: 'https://example.com/action',
|
|
170
|
-
error: null
|
|
171
|
-
});
|
|
172
|
-
expect(await admin.generateVerifyAndChangeEmailLink('old@example.com', 'new@example.com', settings)).toEqual({ data: 'https://example.com/action', error: null });
|
|
173
|
-
expect(generateEmailActionLink).toHaveBeenCalledWith('test-project', {
|
|
174
|
-
requestType: 'VERIFY_AND_CHANGE_EMAIL',
|
|
175
|
-
email: 'old@example.com',
|
|
176
|
-
newEmail: 'new@example.com',
|
|
177
|
-
returnOobLink: true,
|
|
178
|
-
...(settings
|
|
179
|
-
? {
|
|
180
|
-
continueUrl: settings.url,
|
|
181
|
-
canHandleCodeInApp: false
|
|
182
|
-
}
|
|
183
|
-
: {})
|
|
184
|
-
}, 'test-access-token', fetchFn, 'tenant');
|
|
185
|
-
});
|
|
186
|
-
it.each([
|
|
187
|
-
['invalid', 'new@example.com'],
|
|
188
|
-
['old@example.com', 'invalid'],
|
|
189
|
-
['old@example.com', undefined]
|
|
190
|
-
])('rejects invalid addresses %s/%s', async (email, newEmail) => {
|
|
191
|
-
const result = await auth.generateVerifyAndChangeEmailLink(email, newEmail);
|
|
192
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_API_INVALID_ARGUMENT.code);
|
|
193
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
194
|
-
});
|
|
195
|
-
it('returns credential errors without requesting a link', async () => {
|
|
196
|
-
mockedGetToken.mockResolvedValue({
|
|
197
|
-
data: null,
|
|
198
|
-
error: new Error('credentials')
|
|
199
|
-
});
|
|
200
|
-
expect((await auth.generateVerifyAndChangeEmailLink('old@example.com', 'new@example.com')).error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED
|
|
201
|
-
.code);
|
|
202
|
-
expect(generateEmailActionLink).not.toHaveBeenCalled();
|
|
203
|
-
});
|
|
204
|
-
it('wraps endpoint failures with their cause', async () => {
|
|
205
|
-
const cause = new Error('email already exists');
|
|
206
|
-
mockedGetToken.mockResolvedValue({
|
|
207
|
-
data: mockGoogleTokenResponse,
|
|
208
|
-
error: null
|
|
209
|
-
});
|
|
210
|
-
vi.mocked(generateEmailActionLink).mockResolvedValue({
|
|
211
|
-
data: null,
|
|
212
|
-
error: cause
|
|
213
|
-
});
|
|
214
|
-
const result = await auth.generateVerifyAndChangeEmailLink('old@example.com', 'new@example.com');
|
|
215
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_EMAIL_ACTION_LINK_FAILED.code);
|
|
216
|
-
expect(result.error?.cause).toBe(cause);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
describe('setCustomUserClaims', () => {
|
|
220
|
-
beforeEach(() => {
|
|
221
|
-
mockedGetToken.mockResolvedValue({
|
|
222
|
-
data: mockGoogleTokenResponse,
|
|
223
|
-
error: null
|
|
224
|
-
});
|
|
225
|
-
vi.mocked(updateAccountAdmin).mockResolvedValue({
|
|
226
|
-
data: { localId: 'uid' },
|
|
227
|
-
error: null
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
it('uses the existing update helper with tenant and custom fetch', async () => {
|
|
231
|
-
const customFetch = vi.fn();
|
|
232
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, 'tenant', customFetch);
|
|
233
|
-
expect(await instance.setCustomUserClaims('uid', { role: 'editor' })).toEqual({ data: undefined, error: null });
|
|
234
|
-
expect(updateAccountAdmin).toHaveBeenCalledExactlyOnceWith('test-project', 'uid', { customAttributes: '{"role":"editor"}' }, 'test-access-token', customFetch, 'tenant');
|
|
235
|
-
expect(getAccountInfo).not.toHaveBeenCalled();
|
|
236
|
-
});
|
|
237
|
-
it('clears claims with null', async () => {
|
|
238
|
-
expect((await auth.setCustomUserClaims('uid', null)).error).toBeNull();
|
|
239
|
-
expect(updateAccountAdmin).toHaveBeenCalledWith('test-project', 'uid', { customAttributes: '{}' }, 'test-access-token', undefined, undefined);
|
|
240
|
-
});
|
|
241
|
-
it('validates UID and claims before authentication', async () => {
|
|
242
|
-
expect((await auth.setCustomUserClaims('', {})).error).not.toBeNull();
|
|
243
|
-
expect((await auth.setCustomUserClaims('a'.repeat(129), {})).error).not.toBeNull();
|
|
244
|
-
expect((await auth.setCustomUserClaims('uid', { sub: 'reserved' }))
|
|
245
|
-
.error).not.toBeNull();
|
|
246
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
247
|
-
expect(updateAccountAdmin).not.toHaveBeenCalled();
|
|
248
|
-
});
|
|
249
|
-
it('uses cached credentials', async () => {
|
|
250
|
-
const cache = {
|
|
251
|
-
getCache: vi.fn().mockResolvedValue(mockGoogleTokenResponse),
|
|
252
|
-
setCache: vi.fn()
|
|
253
|
-
};
|
|
254
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, undefined, undefined, cache);
|
|
255
|
-
expect((await instance.setCustomUserClaims('uid', {})).error).toBeNull();
|
|
256
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
257
|
-
});
|
|
258
|
-
it('returns credential failures without updating', async () => {
|
|
259
|
-
const error = new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED);
|
|
260
|
-
mockedGetToken.mockResolvedValue({ data: null, error });
|
|
261
|
-
expect(await auth.setCustomUserClaims('uid', {})).toEqual({
|
|
262
|
-
data: null,
|
|
263
|
-
error
|
|
264
|
-
});
|
|
265
|
-
expect(updateAccountAdmin).not.toHaveBeenCalled();
|
|
266
|
-
});
|
|
267
|
-
it('guards against missing access tokens', async () => {
|
|
268
|
-
mockedGetToken.mockResolvedValue({
|
|
269
|
-
data: {},
|
|
270
|
-
error: null
|
|
271
|
-
});
|
|
272
|
-
expect((await auth.setCustomUserClaims('uid', {})).error?.code).toBe('auth/admin-no-token-returned');
|
|
273
|
-
expect(updateAccountAdmin).not.toHaveBeenCalled();
|
|
274
|
-
});
|
|
275
|
-
it('preserves endpoint errors including user-not-found', async () => {
|
|
276
|
-
const cause = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_USER_NOT_FOUND);
|
|
277
|
-
vi.mocked(updateAccountAdmin).mockResolvedValue({
|
|
278
|
-
data: null,
|
|
279
|
-
error: cause
|
|
280
|
-
});
|
|
281
|
-
const result = await auth.setCustomUserClaims('missing', {});
|
|
282
|
-
expect(result.data).toBeNull();
|
|
283
|
-
expect(result.error?.code).toBe('auth/admin-set-custom-claims-failed');
|
|
284
|
-
expect(result.error?.cause).toBe(cause);
|
|
285
|
-
});
|
|
286
|
-
it('rejects missing confirmation data', async () => {
|
|
287
|
-
vi.mocked(updateAccountAdmin).mockResolvedValue({
|
|
288
|
-
data: { localId: '' },
|
|
289
|
-
error: null
|
|
290
|
-
});
|
|
291
|
-
expect((await auth.setCustomUserClaims('uid', {})).error?.code).toBe('auth/admin-set-custom-claims-failed');
|
|
292
|
-
});
|
|
293
|
-
it('returns network and credential exceptions instead of rejecting', async () => {
|
|
294
|
-
const cause = new Error('offline');
|
|
295
|
-
vi.mocked(updateAccountAdmin).mockRejectedValue(cause);
|
|
296
|
-
expect((await auth.setCustomUserClaims('uid', {})).error?.cause).toBe(cause);
|
|
297
|
-
mockedGetToken.mockRejectedValue('token failure');
|
|
298
|
-
expect((await auth.setCustomUserClaims('uid', {})).error?.cause).toBeInstanceOf(Error);
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
describe('phone lookup and bulk user management', () => {
|
|
302
|
-
beforeEach(() => {
|
|
303
|
-
mockedGetToken.mockResolvedValue({
|
|
304
|
-
data: mockGoogleTokenResponse,
|
|
305
|
-
error: null
|
|
306
|
-
});
|
|
307
|
-
mockedGetAccountInfo.mockResolvedValue({
|
|
308
|
-
data: mockUserRecord,
|
|
309
|
-
error: null
|
|
310
|
-
});
|
|
311
|
-
vi.mocked(deleteAccountsAdmin).mockResolvedValue({
|
|
312
|
-
data: {},
|
|
313
|
-
error: null
|
|
314
|
-
});
|
|
315
|
-
vi.mocked(importAccountsAdmin).mockResolvedValue({
|
|
316
|
-
data: {},
|
|
317
|
-
error: null
|
|
318
|
-
});
|
|
319
|
-
});
|
|
320
|
-
it('looks up a phone number and returns a converted user record', async () => {
|
|
321
|
-
const customFetch = vi.fn();
|
|
322
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, 'tenant', customFetch);
|
|
323
|
-
const result = await instance.getUserByPhoneNumber('+15555550100');
|
|
324
|
-
expect(result.error).toBeNull();
|
|
325
|
-
expect(result.data?.uid).toBe('uid-1');
|
|
326
|
-
expect(result.data?.toJSON()).toMatchObject({ uid: 'uid-1' });
|
|
327
|
-
expect(getAccountInfo).toHaveBeenCalledWith({ phoneNumber: '+15555550100' }, 'test-access-token', 'test-project', 'tenant', customFetch);
|
|
328
|
-
});
|
|
329
|
-
it('returns user-not-found when no phone match exists', async () => {
|
|
330
|
-
mockedGetAccountInfo.mockResolvedValue({ data: null, error: null });
|
|
331
|
-
expect((await auth.getUserByPhoneNumber('+15555550100')).error?.code).toBe('auth/admin-user-not-found');
|
|
332
|
-
});
|
|
333
|
-
it.each(['', '555', null, 1])('rejects invalid phone number %s before authentication', async (phone) => {
|
|
334
|
-
expect((await auth.getUserByPhoneNumber(phone)).error).toBeInstanceOf(FirebaseEdgeError);
|
|
335
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
336
|
-
});
|
|
337
|
-
it('deletes a batch with tenant, custom fetch, counts, and indexed errors', async () => {
|
|
338
|
-
const customFetch = vi.fn();
|
|
339
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, 'tenant', customFetch);
|
|
340
|
-
vi.mocked(deleteAccountsAdmin).mockResolvedValue({
|
|
341
|
-
data: { errors: [{ index: 1, message: 'failure' }] },
|
|
342
|
-
error: null
|
|
343
|
-
});
|
|
344
|
-
const result = await instance.deleteUsers([
|
|
345
|
-
'one',
|
|
346
|
-
'two',
|
|
347
|
-
'missing'
|
|
348
|
-
]);
|
|
349
|
-
expect(result.error).toBeNull();
|
|
350
|
-
expect(result.data).toMatchObject({
|
|
351
|
-
successCount: 2,
|
|
352
|
-
failureCount: 1,
|
|
353
|
-
errors: [{ index: 1, error: { message: 'failure' } }]
|
|
354
|
-
});
|
|
355
|
-
expect(deleteAccountsAdmin).toHaveBeenCalledWith('test-project', ['one', 'two', 'missing'], 'test-access-token', customFetch, 'tenant');
|
|
356
|
-
});
|
|
357
|
-
it('returns empty delete and import results without network requests', async () => {
|
|
358
|
-
const result = {
|
|
359
|
-
data: { successCount: 0, failureCount: 0, errors: [] },
|
|
360
|
-
error: null
|
|
361
|
-
};
|
|
362
|
-
expect(await auth.deleteUsers([])).toEqual(result);
|
|
363
|
-
expect(await auth.importUsers([])).toEqual(result);
|
|
364
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
365
|
-
});
|
|
366
|
-
it('rejects invalid delete batches and oversized imports before authentication', async () => {
|
|
367
|
-
expect((await auth.deleteUsers(['one', ''])).error).not.toBeNull();
|
|
368
|
-
expect((await auth.deleteUsers(Array(1001).fill('uid'))).error).not.toBeNull();
|
|
369
|
-
expect((await auth.importUsers(Array(1001).fill({ uid: 'uid' }))).error).not.toBeNull();
|
|
370
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
371
|
-
});
|
|
372
|
-
it('merges local and server import failures using original indices', async () => {
|
|
373
|
-
const customFetch = vi.fn();
|
|
374
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, 'tenant', customFetch);
|
|
375
|
-
vi.mocked(importAccountsAdmin).mockResolvedValue({
|
|
376
|
-
data: { error: [{ index: 1, message: 'DUPLICATE_LOCAL_ID' }] },
|
|
377
|
-
error: null
|
|
378
|
-
});
|
|
379
|
-
const result = await instance.importUsers([
|
|
380
|
-
{ uid: '' },
|
|
381
|
-
{ uid: 'one' },
|
|
382
|
-
{ uid: 'two' }
|
|
383
|
-
]);
|
|
384
|
-
expect(result.error).toBeNull();
|
|
385
|
-
expect(result.data).toMatchObject({
|
|
386
|
-
successCount: 1,
|
|
387
|
-
failureCount: 2,
|
|
388
|
-
errors: [{ index: 0 }, { index: 2 }]
|
|
389
|
-
});
|
|
390
|
-
expect(importAccountsAdmin).toHaveBeenCalledWith('test-project', { users: [{ localId: 'one' }, { localId: 'two' }] }, 'test-access-token', customFetch, 'tenant');
|
|
391
|
-
});
|
|
392
|
-
it('returns all-invalid imports locally and rejects missing password settings', async () => {
|
|
393
|
-
expect(await auth.importUsers([{ uid: '' }])).toMatchObject({
|
|
394
|
-
data: { successCount: 0, failureCount: 1 },
|
|
395
|
-
error: null
|
|
396
|
-
});
|
|
397
|
-
expect((await auth.importUsers([
|
|
398
|
-
{ uid: 'uid', passwordHash: new Uint8Array([1]) }
|
|
399
|
-
])).error).not.toBeNull();
|
|
400
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
401
|
-
expect(importAccountsAdmin).not.toHaveBeenCalled();
|
|
402
|
-
});
|
|
403
|
-
it('sends the selected hashing configuration with password imports', async () => {
|
|
404
|
-
await auth.importUsers([{ uid: 'uid', passwordHash: new Uint8Array([1]) }], { hash: { algorithm: 'BCRYPT' } });
|
|
405
|
-
expect(importAccountsAdmin).toHaveBeenCalledWith('test-project', {
|
|
406
|
-
users: [{ localId: 'uid', passwordHash: 'AQ==' }],
|
|
407
|
-
hashAlgorithm: 'BCRYPT'
|
|
408
|
-
}, 'test-access-token', undefined, undefined);
|
|
409
|
-
});
|
|
410
|
-
it('uses cached credentials for all three methods', async () => {
|
|
411
|
-
const cache = {
|
|
412
|
-
getCache: vi.fn().mockResolvedValue(mockGoogleTokenResponse),
|
|
413
|
-
setCache: vi.fn()
|
|
414
|
-
};
|
|
415
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, undefined, undefined, cache);
|
|
416
|
-
expect((await instance.getUserByPhoneNumber('+15555550100')).error).toBeNull();
|
|
417
|
-
expect((await instance.deleteUsers(['uid'])).error).toBeNull();
|
|
418
|
-
expect((await instance.importUsers([{ uid: 'uid' }])).error).toBeNull();
|
|
419
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
420
|
-
});
|
|
421
|
-
const operations = [
|
|
422
|
-
{
|
|
423
|
-
name: 'phone',
|
|
424
|
-
run: () => auth.getUserByPhoneNumber('+15555550100'),
|
|
425
|
-
endpoint: vi.mocked(getAccountInfo)
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
name: 'delete',
|
|
429
|
-
run: () => auth.deleteUsers(['uid']),
|
|
430
|
-
endpoint: vi.mocked(deleteAccountsAdmin)
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
name: 'import',
|
|
434
|
-
run: () => auth.importUsers([{ uid: 'uid' }]),
|
|
435
|
-
endpoint: vi.mocked(importAccountsAdmin)
|
|
436
|
-
}
|
|
437
|
-
];
|
|
438
|
-
describe.each(operations)('$name failures', ({ run, endpoint }) => {
|
|
439
|
-
it('propagates token failures without calling the endpoint', async () => {
|
|
440
|
-
const error = new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED);
|
|
441
|
-
mockedGetToken.mockResolvedValue({ data: null, error });
|
|
442
|
-
expect(await run()).toEqual({ data: null, error });
|
|
443
|
-
expect(endpoint).not.toHaveBeenCalled();
|
|
444
|
-
});
|
|
445
|
-
it('guards against a missing access token', async () => {
|
|
446
|
-
mockedGetToken.mockResolvedValue({
|
|
447
|
-
data: {},
|
|
448
|
-
error: null
|
|
449
|
-
});
|
|
450
|
-
expect((await run()).error?.code).toBe('auth/admin-no-token-returned');
|
|
451
|
-
expect(endpoint).not.toHaveBeenCalled();
|
|
452
|
-
});
|
|
453
|
-
it('returns endpoint errors and network exceptions', async () => {
|
|
454
|
-
const error = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_PERMISSION_DENIED);
|
|
455
|
-
endpoint.mockResolvedValue({ data: null, error });
|
|
456
|
-
expect(await run()).toMatchObject({
|
|
457
|
-
data: null,
|
|
458
|
-
error: expect.any(FirebaseEdgeError)
|
|
459
|
-
});
|
|
460
|
-
endpoint.mockRejectedValue(new Error('offline'));
|
|
461
|
-
expect(await run()).toMatchObject({
|
|
462
|
-
data: null,
|
|
463
|
-
error: expect.any(FirebaseEdgeError)
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
});
|
|
467
|
-
it('returns errors for corrupt bulk responses instead of misleading counts', async () => {
|
|
468
|
-
vi.mocked(deleteAccountsAdmin).mockResolvedValue({
|
|
469
|
-
data: { errors: [{ index: 5 }] },
|
|
470
|
-
error: null
|
|
471
|
-
});
|
|
472
|
-
vi.mocked(importAccountsAdmin).mockResolvedValue({
|
|
473
|
-
data: { error: [{ index: 5 }] },
|
|
474
|
-
error: null
|
|
475
|
-
});
|
|
476
|
-
expect((await auth.deleteUsers(['uid'])).error).not.toBeNull();
|
|
477
|
-
expect((await auth.importUsers([{ uid: 'uid' }])).error).not.toBeNull();
|
|
478
|
-
});
|
|
479
|
-
});
|
|
480
|
-
describe('getUsers', () => {
|
|
481
|
-
beforeEach(() => {
|
|
482
|
-
mockedGetToken.mockResolvedValue({
|
|
483
|
-
data: mockGoogleTokenResponse,
|
|
484
|
-
error: null
|
|
485
|
-
});
|
|
486
|
-
vi.mocked(getAccountsInfo).mockResolvedValue({
|
|
487
|
-
data: {},
|
|
488
|
-
error: null
|
|
489
|
-
});
|
|
490
|
-
});
|
|
491
|
-
it('returns records and unmatched identifiers using one batch request', async () => {
|
|
492
|
-
const customFetch = vi.fn();
|
|
493
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, 'tenant', customFetch);
|
|
494
|
-
const ids = [{ uid: 'uid' }, { email: 'missing@example.com' }];
|
|
495
|
-
vi.mocked(getAccountsInfo).mockResolvedValue({
|
|
496
|
-
data: { users: [{ localId: 'uid' }] },
|
|
497
|
-
error: null
|
|
498
|
-
});
|
|
499
|
-
const { data, error } = await instance.getUsers(ids);
|
|
500
|
-
expect(error).toBeNull();
|
|
501
|
-
expect(data?.users[0]?.uid).toBe('uid');
|
|
502
|
-
expect(data?.notFound).toEqual([ids[1]]);
|
|
503
|
-
expect(getAccountsInfo).toHaveBeenCalledExactlyOnceWith({ localId: ['uid'], email: ['missing@example.com'] }, 'test-access-token', 'test-project', 'tenant', customFetch);
|
|
504
|
-
});
|
|
505
|
-
it('returns an empty batch without authentication or requests', async () => {
|
|
506
|
-
expect(await auth.getUsers([])).toEqual({
|
|
507
|
-
data: { users: [], notFound: [] },
|
|
508
|
-
error: null
|
|
509
|
-
});
|
|
510
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
511
|
-
expect(getAccountsInfo).not.toHaveBeenCalled();
|
|
512
|
-
});
|
|
513
|
-
it('rejects invalid inputs before authentication', async () => {
|
|
514
|
-
expect((await auth.getUsers([{ uid: '' }])).error).toBeInstanceOf(FirebaseEdgeError);
|
|
515
|
-
expect((await auth.getUsers(Array(101).fill({ uid: 'uid' }))).error).toBeInstanceOf(FirebaseEdgeError);
|
|
516
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
517
|
-
expect(getAccountsInfo).not.toHaveBeenCalled();
|
|
518
|
-
});
|
|
519
|
-
it('returns all identifiers in notFound when there are no matches', async () => {
|
|
520
|
-
expect(await auth.getUsers([{ uid: 'missing' }])).toEqual({
|
|
521
|
-
data: { users: [], notFound: [{ uid: 'missing' }] },
|
|
522
|
-
error: null
|
|
523
|
-
});
|
|
524
|
-
});
|
|
525
|
-
it('uses cached credentials', async () => {
|
|
526
|
-
const cache = {
|
|
527
|
-
getCache: vi.fn().mockResolvedValue(mockGoogleTokenResponse),
|
|
528
|
-
setCache: vi.fn()
|
|
529
|
-
};
|
|
530
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, undefined, undefined, cache);
|
|
531
|
-
expect((await instance.getUsers([{ uid: 'uid' }])).error).toBeNull();
|
|
532
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
533
|
-
expect(getAccountsInfo).toHaveBeenCalledTimes(1);
|
|
534
|
-
});
|
|
535
|
-
it('returns credential failures without a lookup', async () => {
|
|
536
|
-
const error = new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED);
|
|
537
|
-
mockedGetToken.mockResolvedValue({ data: null, error });
|
|
538
|
-
expect(await auth.getUsers([{ uid: 'uid' }])).toEqual({
|
|
539
|
-
data: null,
|
|
540
|
-
error
|
|
541
|
-
});
|
|
542
|
-
expect(getAccountsInfo).not.toHaveBeenCalled();
|
|
543
|
-
});
|
|
544
|
-
it('guards against missing access tokens', async () => {
|
|
545
|
-
mockedGetToken.mockResolvedValue({
|
|
546
|
-
data: {},
|
|
547
|
-
error: null
|
|
548
|
-
});
|
|
549
|
-
expect((await auth.getUsers([{ uid: 'uid' }])).error?.code).toBe('auth/admin-no-token-returned');
|
|
550
|
-
expect(getAccountsInfo).not.toHaveBeenCalled();
|
|
551
|
-
});
|
|
552
|
-
it('retains mapped endpoint failures as the cause', async () => {
|
|
553
|
-
const cause = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_PERMISSION_DENIED);
|
|
554
|
-
vi.mocked(getAccountsInfo).mockResolvedValue({
|
|
555
|
-
data: null,
|
|
556
|
-
error: cause
|
|
557
|
-
});
|
|
558
|
-
const result = await auth.getUsers([{ uid: 'uid' }]);
|
|
559
|
-
expect(result.data).toBeNull();
|
|
560
|
-
expect(result.error?.cause).toBe(cause);
|
|
561
|
-
});
|
|
562
|
-
it.each([null, 'not JSON', { users: [{}] }, { users: 'invalid' }])('returns structured errors for malformed responses %j', async (data) => {
|
|
563
|
-
vi.mocked(getAccountsInfo).mockResolvedValue({
|
|
564
|
-
data,
|
|
565
|
-
error: null
|
|
566
|
-
});
|
|
567
|
-
const result = await auth.getUsers([{ uid: 'uid' }]);
|
|
568
|
-
expect(result.data).toBeNull();
|
|
569
|
-
expect(result.error?.code).toBe('auth/admin-user-lookup-failed');
|
|
570
|
-
});
|
|
571
|
-
it('returns network and token exceptions rather than rejecting', async () => {
|
|
572
|
-
vi.mocked(getAccountsInfo).mockRejectedValue(new Error('offline'));
|
|
573
|
-
expect((await auth.getUsers([{ uid: 'uid' }])).error?.cause).toBeInstanceOf(Error);
|
|
574
|
-
mockedGetToken.mockRejectedValue('token failure');
|
|
575
|
-
expect((await auth.getUsers([{ uid: 'uid' }])).error?.cause).toBeInstanceOf(Error);
|
|
576
|
-
});
|
|
577
|
-
});
|
|
578
|
-
describe('user management', () => {
|
|
579
|
-
beforeEach(() => {
|
|
580
|
-
mockedGetToken.mockResolvedValue({
|
|
581
|
-
data: mockGoogleTokenResponse,
|
|
582
|
-
error: null
|
|
583
|
-
});
|
|
584
|
-
mockedGetAccountInfo.mockResolvedValue({
|
|
585
|
-
data: mockUserRecord,
|
|
586
|
-
error: null
|
|
587
|
-
});
|
|
588
|
-
vi.mocked(createAccountAdmin).mockResolvedValue({
|
|
589
|
-
data: { localId: 'uid-1' },
|
|
590
|
-
error: null
|
|
591
|
-
});
|
|
592
|
-
vi.mocked(updateAccountAdmin).mockResolvedValue({
|
|
593
|
-
data: { localId: 'uid-1' },
|
|
594
|
-
error: null
|
|
595
|
-
});
|
|
596
|
-
vi.mocked(deleteAccountAdmin).mockResolvedValue({ error: null });
|
|
597
|
-
});
|
|
598
|
-
it('creates a user and reads the full record with the same credentials', async () => {
|
|
599
|
-
const customFetch = vi.fn();
|
|
600
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, 'tenant', customFetch);
|
|
601
|
-
const result = await instance.createUser({
|
|
602
|
-
email: 'test@example.com',
|
|
603
|
-
photoURL: 'https://example.com/photo',
|
|
604
|
-
disabled: false
|
|
605
|
-
});
|
|
606
|
-
expect(result.error).toBeNull();
|
|
607
|
-
expect(result.data?.uid).toBe('uid-1');
|
|
608
|
-
expect(result.data?.toJSON()).toMatchObject({
|
|
609
|
-
uid: 'uid-1',
|
|
610
|
-
email: 'test@example.com'
|
|
611
|
-
});
|
|
612
|
-
expect(createAccountAdmin).toHaveBeenCalledWith('test-project', {
|
|
613
|
-
email: 'test@example.com',
|
|
614
|
-
photoUrl: 'https://example.com/photo',
|
|
615
|
-
disabled: false
|
|
616
|
-
}, 'test-access-token', customFetch, 'tenant');
|
|
617
|
-
expect(mockedGetAccountInfo).toHaveBeenCalledWith({ uid: 'uid-1' }, 'test-access-token', 'test-project', 'tenant', customFetch);
|
|
618
|
-
expect(mockedGetToken).toHaveBeenCalledTimes(1);
|
|
619
|
-
});
|
|
620
|
-
it('updates through the existing endpoint with translated fields and tenant', async () => {
|
|
621
|
-
const result = await authWithTenant.updateUser('uid-1', {
|
|
622
|
-
displayName: null,
|
|
623
|
-
photoURL: null,
|
|
624
|
-
phoneNumber: null,
|
|
625
|
-
disabled: true
|
|
626
|
-
});
|
|
627
|
-
expect(result.error).toBeNull();
|
|
628
|
-
expect(result.data?.uid).toBe('uid-1');
|
|
629
|
-
expect(updateAccountAdmin).toHaveBeenCalledWith('test-project', 'uid-1', {
|
|
630
|
-
deleteAttribute: ['DISPLAY_NAME', 'PHOTO_URL'],
|
|
631
|
-
deleteProvider: ['phone'],
|
|
632
|
-
disableUser: true
|
|
633
|
-
}, 'test-access-token', undefined, 'test-tenant-id');
|
|
634
|
-
expect(mockedGetAccountInfo).toHaveBeenCalledWith({ uid: 'uid-1' }, 'test-access-token', 'test-project', 'test-tenant-id', undefined);
|
|
635
|
-
});
|
|
636
|
-
it('deletes without a follow-up lookup and returns void data', async () => {
|
|
637
|
-
expect(await authWithTenant.deleteUser('uid-1')).toEqual({
|
|
638
|
-
data: undefined,
|
|
639
|
-
error: null
|
|
640
|
-
});
|
|
641
|
-
expect(deleteAccountAdmin).toHaveBeenCalledWith('test-project', 'uid-1', 'test-access-token', undefined, 'test-tenant-id');
|
|
642
|
-
expect(mockedGetAccountInfo).not.toHaveBeenCalled();
|
|
643
|
-
});
|
|
644
|
-
it.each(['', 'a'.repeat(129), null])('rejects invalid UID %s before authentication', async (uid) => {
|
|
645
|
-
expect((await auth.updateUser(uid, {})).error).toBeInstanceOf(FirebaseEdgeError);
|
|
646
|
-
expect((await auth.deleteUser(uid)).error).toBeInstanceOf(FirebaseEdgeError);
|
|
647
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
648
|
-
});
|
|
649
|
-
it('rejects invalid create and update properties before authentication', async () => {
|
|
650
|
-
expect((await auth.createUser({ email: 'invalid' })).error).toBeInstanceOf(FirebaseEdgeError);
|
|
651
|
-
expect((await auth.updateUser('uid', { password: 'short' })).error).toBeInstanceOf(FirebaseEdgeError);
|
|
652
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
653
|
-
});
|
|
654
|
-
const operations = [
|
|
655
|
-
{
|
|
656
|
-
name: 'create',
|
|
657
|
-
run: () => auth.createUser({}),
|
|
658
|
-
endpoint: vi.mocked(createAccountAdmin)
|
|
659
|
-
},
|
|
660
|
-
{
|
|
661
|
-
name: 'update',
|
|
662
|
-
run: () => auth.updateUser('uid-1', {}),
|
|
663
|
-
endpoint: vi.mocked(updateAccountAdmin)
|
|
664
|
-
},
|
|
665
|
-
{
|
|
666
|
-
name: 'delete',
|
|
667
|
-
run: () => auth.deleteUser('uid-1'),
|
|
668
|
-
endpoint: vi.mocked(deleteAccountAdmin)
|
|
669
|
-
}
|
|
670
|
-
];
|
|
671
|
-
describe.each(operations)('$name errors', ({ name, run, endpoint }) => {
|
|
672
|
-
it('returns credential errors without a write', async () => {
|
|
673
|
-
const error = new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED);
|
|
674
|
-
mockedGetToken.mockResolvedValue({ data: null, error });
|
|
675
|
-
expect(await run()).toEqual({ data: null, error });
|
|
676
|
-
expect(endpoint).not.toHaveBeenCalled();
|
|
677
|
-
});
|
|
678
|
-
it('guards against missing credentials', async () => {
|
|
679
|
-
mockedGetToken.mockResolvedValue({
|
|
680
|
-
data: {},
|
|
681
|
-
error: null
|
|
682
|
-
});
|
|
683
|
-
expect((await run()).error?.code).toBe('auth/admin-no-token-returned');
|
|
684
|
-
expect(endpoint).not.toHaveBeenCalled();
|
|
685
|
-
});
|
|
686
|
-
it('preserves API errors as the cause without a readback', async () => {
|
|
687
|
-
const cause = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_USER_NOT_FOUND);
|
|
688
|
-
endpoint.mockResolvedValue({ data: null, error: cause });
|
|
689
|
-
const result = await run();
|
|
690
|
-
expect(result.data).toBeNull();
|
|
691
|
-
expect(result.error?.code).toBe(`auth/admin-${name}-user-failed`);
|
|
692
|
-
expect(result.error?.cause).toBe(cause);
|
|
693
|
-
expect(mockedGetAccountInfo).not.toHaveBeenCalled();
|
|
694
|
-
});
|
|
695
|
-
it('returns transport exceptions instead of rejecting', async () => {
|
|
696
|
-
const cause = new Error('offline');
|
|
697
|
-
endpoint.mockRejectedValue(cause);
|
|
698
|
-
const result = await run();
|
|
699
|
-
expect(result.data).toBeNull();
|
|
700
|
-
expect(result.error?.cause).toBe(cause);
|
|
701
|
-
});
|
|
702
|
-
it('returns unexpected token exceptions', async () => {
|
|
703
|
-
mockedGetToken.mockRejectedValue('token failure');
|
|
704
|
-
const result = await run();
|
|
705
|
-
expect(result.data).toBeNull();
|
|
706
|
-
expect(result.error?.code).toBe(`auth/admin-${name}-user-failed`);
|
|
707
|
-
});
|
|
708
|
-
});
|
|
709
|
-
describe.each(operations.slice(0, 2))('$name readback', ({ run, endpoint }) => {
|
|
710
|
-
it('requires a UID in the write response', async () => {
|
|
711
|
-
endpoint.mockResolvedValue({
|
|
712
|
-
data: { localId: '' },
|
|
713
|
-
error: null
|
|
714
|
-
});
|
|
715
|
-
expect((await run()).error).toBeInstanceOf(FirebaseEdgeError);
|
|
716
|
-
expect(mockedGetAccountInfo).not.toHaveBeenCalled();
|
|
717
|
-
});
|
|
718
|
-
it('returns lookup errors', async () => {
|
|
719
|
-
const error = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_USER_NOT_FOUND);
|
|
720
|
-
mockedGetAccountInfo.mockResolvedValue({
|
|
721
|
-
data: null,
|
|
722
|
-
error
|
|
723
|
-
});
|
|
724
|
-
expect(await run()).toEqual({ data: null, error });
|
|
725
|
-
});
|
|
726
|
-
it('handles a missing user in the lookup response', async () => {
|
|
727
|
-
mockedGetAccountInfo.mockResolvedValue({
|
|
728
|
-
data: null,
|
|
729
|
-
error: null
|
|
730
|
-
});
|
|
731
|
-
expect((await run()).error?.code).toBe('auth/admin-user-record-not-found');
|
|
732
|
-
});
|
|
733
|
-
it('returns conversion errors rather than rejecting', async () => {
|
|
734
|
-
mockedGetAccountInfo.mockResolvedValue({
|
|
735
|
-
data: { localId: 'uid', customAttributes: '{' },
|
|
736
|
-
error: null
|
|
737
|
-
});
|
|
738
|
-
expect((await run()).error).toBeInstanceOf(FirebaseEdgeError);
|
|
739
|
-
});
|
|
740
|
-
});
|
|
741
|
-
});
|
|
742
|
-
describe('listUsers', () => {
|
|
743
|
-
const download = vi.mocked(downloadAccount);
|
|
744
|
-
beforeEach(() => {
|
|
745
|
-
mockedGetToken.mockResolvedValue({
|
|
746
|
-
data: mockGoogleTokenResponse,
|
|
747
|
-
error: null
|
|
748
|
-
});
|
|
749
|
-
download.mockResolvedValue({ data: {}, error: null });
|
|
750
|
-
});
|
|
751
|
-
it('requests the default page and converts the result', async () => {
|
|
752
|
-
download.mockResolvedValue({
|
|
753
|
-
data: { users: [{ localId: 'uid' }], nextPageToken: 'next' },
|
|
754
|
-
error: null
|
|
755
|
-
});
|
|
756
|
-
const { data, error } = await auth.listUsers();
|
|
757
|
-
expect(error).toBeNull();
|
|
758
|
-
expect(data?.pageToken).toBe('next');
|
|
759
|
-
expect(data?.users[0]).toMatchObject({
|
|
760
|
-
uid: 'uid',
|
|
761
|
-
emailVerified: false,
|
|
762
|
-
disabled: false
|
|
763
|
-
});
|
|
764
|
-
expect(download).toHaveBeenCalledExactlyOnceWith('test-access-token', 'test-project', 1000, undefined, undefined, undefined);
|
|
765
|
-
});
|
|
766
|
-
it('forwards pagination, tenant, and custom fetch to the endpoint', async () => {
|
|
767
|
-
const customFetch = vi.fn();
|
|
768
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, 'tenant', customFetch);
|
|
769
|
-
expect(await instance.listUsers(25, 'opaque+/=')).toEqual({
|
|
770
|
-
data: { users: [] },
|
|
771
|
-
error: null
|
|
772
|
-
});
|
|
773
|
-
expect(download).toHaveBeenCalledExactlyOnceWith('test-access-token', 'test-project', 25, 'opaque+/=', 'tenant', customFetch);
|
|
774
|
-
expect(mockedGetToken).toHaveBeenCalledWith(serviceAccountKey, customFetch);
|
|
775
|
-
});
|
|
776
|
-
it('defaults an undefined page size and omits an absent page token', async () => {
|
|
777
|
-
expect(await auth.listUsers(undefined)).toEqual({
|
|
778
|
-
data: { users: [] },
|
|
779
|
-
error: null
|
|
780
|
-
});
|
|
781
|
-
});
|
|
782
|
-
it('preserves an explicitly returned empty page token', async () => {
|
|
783
|
-
download.mockResolvedValue({
|
|
784
|
-
data: { nextPageToken: '' },
|
|
785
|
-
error: null
|
|
786
|
-
});
|
|
787
|
-
expect(await auth.listUsers()).toEqual({
|
|
788
|
-
data: { users: [], pageToken: '' },
|
|
789
|
-
error: null
|
|
790
|
-
});
|
|
791
|
-
});
|
|
792
|
-
it.each([0, -1, 1001, 1.5, NaN, Infinity, null, '10'])('rejects invalid page size %s before authentication', async (size) => {
|
|
793
|
-
const result = await auth.listUsers(size);
|
|
794
|
-
expect(result.data).toBeNull();
|
|
795
|
-
expect(result.error?.code).toBe('auth/admin-api-invalid-argument');
|
|
796
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
797
|
-
expect(download).not.toHaveBeenCalled();
|
|
798
|
-
});
|
|
799
|
-
it.each(['', null, 123, {}])('rejects invalid page token %s before authentication', async (pageToken) => {
|
|
800
|
-
const result = await auth.listUsers(25, pageToken);
|
|
801
|
-
expect(result.data).toBeNull();
|
|
802
|
-
expect(result.error?.code).toBe('auth/admin-invalid-page-token');
|
|
803
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
804
|
-
expect(download).not.toHaveBeenCalled();
|
|
805
|
-
});
|
|
806
|
-
it('returns authentication errors without calling the endpoint', async () => {
|
|
807
|
-
const error = new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED);
|
|
808
|
-
mockedGetToken.mockResolvedValue({ data: null, error });
|
|
809
|
-
expect(await auth.listUsers()).toEqual({ data: null, error });
|
|
810
|
-
expect(download).not.toHaveBeenCalled();
|
|
811
|
-
});
|
|
812
|
-
it('uses cached credentials', async () => {
|
|
813
|
-
const cache = {
|
|
814
|
-
getCache: vi.fn().mockResolvedValue(mockGoogleTokenResponse),
|
|
815
|
-
setCache: vi.fn()
|
|
816
|
-
};
|
|
817
|
-
const instance = new FirebaseAdminAuth(serviceAccountKey, undefined, undefined, cache);
|
|
818
|
-
expect((await instance.listUsers()).error).toBeNull();
|
|
819
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
820
|
-
expect(cache.getCache).toHaveBeenCalledWith('__cache');
|
|
821
|
-
expect(download).toHaveBeenCalledWith('test-access-token', 'test-project', 1000, undefined, undefined, undefined);
|
|
822
|
-
});
|
|
823
|
-
it('guards against a missing access token', async () => {
|
|
824
|
-
mockedGetToken.mockResolvedValue({
|
|
825
|
-
data: {},
|
|
826
|
-
error: null
|
|
827
|
-
});
|
|
828
|
-
expect((await auth.listUsers()).error?.code).toBe('auth/admin-no-token-returned');
|
|
829
|
-
expect(download).not.toHaveBeenCalled();
|
|
830
|
-
});
|
|
831
|
-
it('retains a mapped endpoint error as its cause', async () => {
|
|
832
|
-
const cause = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_PERMISSION_DENIED);
|
|
833
|
-
download.mockResolvedValue({ data: null, error: cause });
|
|
834
|
-
const result = await auth.listUsers();
|
|
835
|
-
expect(result.data).toBeNull();
|
|
836
|
-
expect(result.error?.code).toBe('auth/admin-list-users-failed');
|
|
837
|
-
expect(result.error?.cause).toBe(cause);
|
|
838
|
-
});
|
|
839
|
-
it.each([
|
|
840
|
-
null,
|
|
841
|
-
'not JSON',
|
|
842
|
-
{ users: [{ localId: 'uid', customAttributes: '{' }] },
|
|
843
|
-
{ users: [{}] },
|
|
844
|
-
{ users: 'invalid' }
|
|
845
|
-
])('returns an error for invalid endpoint data %j', async (data) => {
|
|
846
|
-
download.mockResolvedValue({ data, error: null });
|
|
847
|
-
const result = await auth.listUsers();
|
|
848
|
-
expect(result.data).toBeNull();
|
|
849
|
-
expect(result.error?.code).toBe('auth/admin-list-users-failed');
|
|
850
|
-
});
|
|
851
|
-
it('returns unexpected endpoint failures rather than rejecting', async () => {
|
|
852
|
-
const cause = new Error('offline');
|
|
853
|
-
download.mockRejectedValue(cause);
|
|
854
|
-
const result = await auth.listUsers();
|
|
855
|
-
expect(result.data).toBeNull();
|
|
856
|
-
expect(result.error?.cause).toBe(cause);
|
|
857
|
-
});
|
|
858
|
-
it('normalizes unexpected authentication failures', async () => {
|
|
859
|
-
mockedGetToken.mockRejectedValue('token failure');
|
|
860
|
-
const result = await auth.listUsers();
|
|
861
|
-
expect(result.data).toBeNull();
|
|
862
|
-
expect(result.error?.cause).toBeInstanceOf(Error);
|
|
863
|
-
});
|
|
864
|
-
});
|
|
865
|
-
describe('getUser', () => {
|
|
866
|
-
it('returns error when getToken returns an error', async () => {
|
|
867
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
868
|
-
data: null,
|
|
869
|
-
error: new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED)
|
|
870
|
-
});
|
|
871
|
-
const result = await auth.getUser('uid-1');
|
|
872
|
-
expect(mockedGetToken).toHaveBeenCalled();
|
|
873
|
-
expect(result.data).toBeNull();
|
|
874
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
875
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED
|
|
876
|
-
.code);
|
|
877
|
-
});
|
|
878
|
-
it('returns error when getToken does not return a token', async () => {
|
|
879
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
880
|
-
data: null,
|
|
881
|
-
error: new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED)
|
|
882
|
-
});
|
|
883
|
-
const result = await auth.getUser('uid-1');
|
|
884
|
-
expect(result.data).toBeNull();
|
|
885
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
886
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED
|
|
887
|
-
.code);
|
|
888
|
-
});
|
|
889
|
-
it('returns user when everything succeeds', async () => {
|
|
890
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
891
|
-
data: mockGoogleTokenResponse,
|
|
892
|
-
error: null
|
|
893
|
-
});
|
|
894
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
895
|
-
data: mockUserRecord,
|
|
896
|
-
error: null
|
|
897
|
-
});
|
|
898
|
-
const result = await auth.getUser('uid-1');
|
|
899
|
-
expect(mockedGetToken).toHaveBeenCalledWith(serviceAccountKey, undefined);
|
|
900
|
-
expect(mockedGetAccountInfo).toHaveBeenCalledWith({ uid: 'uid-1' }, 'test-access-token', 'test-project', undefined, undefined);
|
|
901
|
-
expect(result.data).toEqual(mockUserRecord);
|
|
902
|
-
expect(result.error).toBeNull();
|
|
903
|
-
});
|
|
904
|
-
it('returns error when getAccountInfoByUid fails', async () => {
|
|
905
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
906
|
-
data: mockGoogleTokenResponse,
|
|
907
|
-
error: null
|
|
908
|
-
});
|
|
909
|
-
const error = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_USER_NOT_FOUND);
|
|
910
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
911
|
-
data: null,
|
|
912
|
-
error
|
|
913
|
-
});
|
|
914
|
-
const result = await auth.getUser('uid-1');
|
|
915
|
-
expect(result.data).toBeNull();
|
|
916
|
-
expect(result.error).toEqual(error);
|
|
917
|
-
});
|
|
918
|
-
});
|
|
919
|
-
describe('verifyIdToken', () => {
|
|
920
|
-
it('returns error when verifyJWT fails', async () => {
|
|
921
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
922
|
-
data: null,
|
|
923
|
-
error: new Error('bad token')
|
|
924
|
-
});
|
|
925
|
-
const result = await auth.verifyIdToken('id-token');
|
|
926
|
-
expect(mockedVerifyJWT).toHaveBeenCalledWith('id-token', 'test-project', undefined);
|
|
927
|
-
expect(result.data).toBeNull();
|
|
928
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
929
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_ID_TOKEN_VERIFY_FAILED.code);
|
|
930
|
-
});
|
|
931
|
-
it('returns decoded token when not checking revoked', async () => {
|
|
932
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
933
|
-
data: mockFirebasePayload,
|
|
934
|
-
error: null
|
|
935
|
-
});
|
|
936
|
-
const result = await auth.verifyIdToken('id-token', false);
|
|
937
|
-
expect(result.data).toEqual(mockFirebasePayload);
|
|
938
|
-
expect(result.error).toBeNull();
|
|
939
|
-
});
|
|
940
|
-
it('returns error when user lookup fails during revoked check', async () => {
|
|
941
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
942
|
-
data: mockFirebasePayload,
|
|
943
|
-
error: null
|
|
944
|
-
});
|
|
945
|
-
const userError = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_INTERNAL_ERROR);
|
|
946
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
947
|
-
data: mockGoogleTokenResponse,
|
|
948
|
-
error: null
|
|
949
|
-
});
|
|
950
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
951
|
-
data: null,
|
|
952
|
-
error: userError
|
|
953
|
-
});
|
|
954
|
-
const result = await auth.verifyIdToken('id-token', true);
|
|
955
|
-
expect(result.data).toBeNull();
|
|
956
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
957
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_USER_LOOKUP_FAILED.code);
|
|
958
|
-
});
|
|
959
|
-
it('returns ERR_NO_USER when user is null during revoked check', async () => {
|
|
960
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
961
|
-
data: mockFirebasePayload,
|
|
962
|
-
error: null
|
|
963
|
-
});
|
|
964
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
965
|
-
data: mockGoogleTokenResponse,
|
|
966
|
-
error: null
|
|
967
|
-
});
|
|
968
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
969
|
-
data: null,
|
|
970
|
-
error: null
|
|
971
|
-
});
|
|
972
|
-
const result = await auth.verifyIdToken('id-token', true);
|
|
973
|
-
expect(result.data).toBeNull();
|
|
974
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
975
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_USER_RECORD_NOT_FOUND.code);
|
|
976
|
-
});
|
|
977
|
-
it('returns ERR_USER_DISABLED when user.disabled is true', async () => {
|
|
978
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
979
|
-
data: mockFirebasePayload,
|
|
980
|
-
error: null
|
|
981
|
-
});
|
|
982
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
983
|
-
data: mockGoogleTokenResponse,
|
|
984
|
-
error: null
|
|
985
|
-
});
|
|
986
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
987
|
-
data: { ...mockUserRecord, disabled: true },
|
|
988
|
-
error: null
|
|
989
|
-
});
|
|
990
|
-
const result = await auth.verifyIdToken('id-token', true);
|
|
991
|
-
expect(result.data).toBeNull();
|
|
992
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
993
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_USER_DISABLED.code);
|
|
994
|
-
});
|
|
995
|
-
it('returns ERR_TOKEN_REVOKED when auth_time < tokensValidAfterTime', async () => {
|
|
996
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
997
|
-
data: mockFirebasePayload,
|
|
998
|
-
error: null
|
|
999
|
-
});
|
|
1000
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1001
|
-
data: mockGoogleTokenResponse,
|
|
1002
|
-
error: null
|
|
1003
|
-
});
|
|
1004
|
-
// tokensValidAfterTime far in the future compared to auth_time
|
|
1005
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
1006
|
-
data: {
|
|
1007
|
-
...mockUserRecord,
|
|
1008
|
-
disabled: false,
|
|
1009
|
-
validSince: '2000000'
|
|
1010
|
-
},
|
|
1011
|
-
error: null
|
|
1012
|
-
});
|
|
1013
|
-
const result = await auth.verifyIdToken('id-token', true);
|
|
1014
|
-
expect(result.data).toBeNull();
|
|
1015
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1016
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_ID_TOKEN_REVOKED.code);
|
|
1017
|
-
});
|
|
1018
|
-
it('returns decoded token when not revoked', async () => {
|
|
1019
|
-
const mockPayloadWithLaterAuthTime = {
|
|
1020
|
-
...mockFirebasePayload,
|
|
1021
|
-
auth_time: 2000
|
|
1022
|
-
};
|
|
1023
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
1024
|
-
data: mockPayloadWithLaterAuthTime,
|
|
1025
|
-
error: null
|
|
1026
|
-
});
|
|
1027
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1028
|
-
data: mockGoogleTokenResponse,
|
|
1029
|
-
error: null
|
|
1030
|
-
});
|
|
1031
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
1032
|
-
data: {
|
|
1033
|
-
...mockUserRecord,
|
|
1034
|
-
disabled: false,
|
|
1035
|
-
validSince: '1000'
|
|
1036
|
-
},
|
|
1037
|
-
error: null
|
|
1038
|
-
});
|
|
1039
|
-
const result = await auth.verifyIdToken('id-token', true);
|
|
1040
|
-
expect(result.data).toEqual(mockPayloadWithLaterAuthTime);
|
|
1041
|
-
expect(result.error).toBeNull();
|
|
1042
|
-
});
|
|
1043
|
-
it('returns error when token has no decoded payload', async () => {
|
|
1044
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
1045
|
-
data: null,
|
|
1046
|
-
error: new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_INVALID_ID_TOKEN)
|
|
1047
|
-
});
|
|
1048
|
-
const result = await auth.verifyIdToken('id-token');
|
|
1049
|
-
expect(result.data).toBeNull();
|
|
1050
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1051
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_ID_TOKEN_VERIFY_FAILED.code);
|
|
1052
|
-
});
|
|
1053
|
-
it('returns error when tenant ID does not match', async () => {
|
|
1054
|
-
const mockPayloadWithDifferentTenant = {
|
|
1055
|
-
...mockFirebasePayload,
|
|
1056
|
-
firebase: {
|
|
1057
|
-
...mockFirebasePayload.firebase,
|
|
1058
|
-
tenant: 'different-tenant-id'
|
|
1059
|
-
}
|
|
1060
|
-
};
|
|
1061
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
1062
|
-
data: mockPayloadWithDifferentTenant,
|
|
1063
|
-
error: null
|
|
1064
|
-
});
|
|
1065
|
-
const result = await authWithTenant.verifyIdToken('id-token');
|
|
1066
|
-
expect(result.data).toBeNull();
|
|
1067
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1068
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_TENANT_ID_INVALID.code);
|
|
1069
|
-
});
|
|
1070
|
-
it('returns success when tenant ID matches', async () => {
|
|
1071
|
-
const mockPayloadWithCorrectTenant = {
|
|
1072
|
-
...mockFirebasePayload,
|
|
1073
|
-
firebase: {
|
|
1074
|
-
...mockFirebasePayload.firebase,
|
|
1075
|
-
tenant: 'test-tenant-id'
|
|
1076
|
-
}
|
|
1077
|
-
};
|
|
1078
|
-
mockedVerifyJWT.mockResolvedValueOnce({
|
|
1079
|
-
data: mockPayloadWithCorrectTenant,
|
|
1080
|
-
error: null
|
|
1081
|
-
});
|
|
1082
|
-
const result = await authWithTenant.verifyIdToken('id-token', false);
|
|
1083
|
-
expect(result.data).toEqual(mockPayloadWithCorrectTenant);
|
|
1084
|
-
expect(result.error).toBeNull();
|
|
1085
|
-
});
|
|
1086
|
-
});
|
|
1087
|
-
describe.each([
|
|
1088
|
-
['generatePasswordResetLink', 'PASSWORD_RESET'],
|
|
1089
|
-
['generateEmailVerificationLink', 'VERIFY_EMAIL'],
|
|
1090
|
-
['generateSignInWithEmailLink', 'EMAIL_SIGNIN']
|
|
1091
|
-
])('%s', (method, requestType) => {
|
|
1092
|
-
const settings = {
|
|
1093
|
-
url: 'https://example.com/finish',
|
|
1094
|
-
handleCodeInApp: true
|
|
1095
|
-
};
|
|
1096
|
-
it('returns the generated link and forwards credentials, settings, tenant and fetch', async () => {
|
|
1097
|
-
const fetchFn = vi.fn();
|
|
1098
|
-
const admin = new FirebaseAdminAuth(serviceAccountKey, 'tenant', fetchFn);
|
|
1099
|
-
mockedGetToken.mockResolvedValue({
|
|
1100
|
-
data: mockGoogleTokenResponse,
|
|
1101
|
-
error: null
|
|
1102
|
-
});
|
|
1103
|
-
vi.mocked(generateEmailActionLink).mockResolvedValue({
|
|
1104
|
-
data: 'https://example.com/action',
|
|
1105
|
-
error: null
|
|
1106
|
-
});
|
|
1107
|
-
expect(await admin[method]('person@example.com', settings)).toEqual({ data: 'https://example.com/action', error: null });
|
|
1108
|
-
expect(generateEmailActionLink).toHaveBeenCalledWith('test-project', {
|
|
1109
|
-
requestType,
|
|
1110
|
-
email: 'person@example.com',
|
|
1111
|
-
returnOobLink: true,
|
|
1112
|
-
continueUrl: settings.url,
|
|
1113
|
-
canHandleCodeInApp: true
|
|
1114
|
-
}, 'test-access-token', fetchFn, 'tenant');
|
|
1115
|
-
});
|
|
1116
|
-
it('rejects invalid input before obtaining credentials', async () => {
|
|
1117
|
-
expect((await auth[method]('invalid', settings)).error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_API_INVALID_ARGUMENT.code);
|
|
1118
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
1119
|
-
expect(generateEmailActionLink).not.toHaveBeenCalled();
|
|
1120
|
-
});
|
|
1121
|
-
it('returns authentication errors without calling the endpoint', async () => {
|
|
1122
|
-
mockedGetToken.mockResolvedValue({
|
|
1123
|
-
data: null,
|
|
1124
|
-
error: new Error('credentials')
|
|
1125
|
-
});
|
|
1126
|
-
expect((await auth[method]('person@example.com', settings)).error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED
|
|
1127
|
-
.code);
|
|
1128
|
-
expect(generateEmailActionLink).not.toHaveBeenCalled();
|
|
1129
|
-
});
|
|
1130
|
-
it('wraps endpoint errors and preserves the cause', async () => {
|
|
1131
|
-
const cause = new Error('email not found');
|
|
1132
|
-
mockedGetToken.mockResolvedValue({
|
|
1133
|
-
data: mockGoogleTokenResponse,
|
|
1134
|
-
error: null
|
|
1135
|
-
});
|
|
1136
|
-
vi.mocked(generateEmailActionLink).mockResolvedValue({
|
|
1137
|
-
data: null,
|
|
1138
|
-
error: cause
|
|
1139
|
-
});
|
|
1140
|
-
const result = await auth[method]('person@example.com', settings);
|
|
1141
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_EMAIL_ACTION_LINK_FAILED.code);
|
|
1142
|
-
expect(result.error?.cause).toBe(cause);
|
|
1143
|
-
});
|
|
1144
|
-
it('returns unexpected failures using the package error convention', async () => {
|
|
1145
|
-
mockedGetToken.mockResolvedValue({
|
|
1146
|
-
data: mockGoogleTokenResponse,
|
|
1147
|
-
error: null
|
|
1148
|
-
});
|
|
1149
|
-
vi.mocked(generateEmailActionLink).mockRejectedValue(new Error('network'));
|
|
1150
|
-
expect((await auth[method]('person@example.com', settings)).error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_EMAIL_ACTION_LINK_FAILED.code);
|
|
1151
|
-
});
|
|
1152
|
-
});
|
|
1153
|
-
it.each([
|
|
1154
|
-
'generatePasswordResetLink',
|
|
1155
|
-
'generateEmailVerificationLink'
|
|
1156
|
-
])('%s allows omitted settings', async (method) => {
|
|
1157
|
-
mockedGetToken.mockResolvedValue({
|
|
1158
|
-
data: mockGoogleTokenResponse,
|
|
1159
|
-
error: null
|
|
1160
|
-
});
|
|
1161
|
-
vi.mocked(generateEmailActionLink).mockResolvedValue({
|
|
1162
|
-
data: 'https://example.com/action',
|
|
1163
|
-
error: null
|
|
1164
|
-
});
|
|
1165
|
-
expect((await auth[method]('person@example.com')).error).toBeNull();
|
|
1166
|
-
expect(generateEmailActionLink).toHaveBeenCalledWith('test-project', expect.not.objectContaining({ continueUrl: expect.anything() }), 'test-access-token', undefined, undefined);
|
|
1167
|
-
});
|
|
1168
|
-
it('requires settings when generating an email sign-in link', async () => {
|
|
1169
|
-
const result =
|
|
1170
|
-
// @ts-expect-error Settings are required for email sign-in.
|
|
1171
|
-
await auth.generateSignInWithEmailLink('person@example.com');
|
|
1172
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_API_INVALID_ARGUMENT.code);
|
|
1173
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
1174
|
-
});
|
|
1175
|
-
it.each([
|
|
1176
|
-
'generatePasswordResetLink',
|
|
1177
|
-
'generateEmailVerificationLink',
|
|
1178
|
-
'generateSignInWithEmailLink',
|
|
1179
|
-
'createSessionCookie'
|
|
1180
|
-
])('%s rejects missing access tokens', async (method) => {
|
|
1181
|
-
mockedGetToken.mockResolvedValue({
|
|
1182
|
-
data: { ...mockGoogleTokenResponse, access_token: '' },
|
|
1183
|
-
error: null
|
|
1184
|
-
});
|
|
1185
|
-
const result = method === 'createSessionCookie'
|
|
1186
|
-
? await auth.createSessionCookie('id-token', {
|
|
1187
|
-
expiresIn: 300000
|
|
1188
|
-
})
|
|
1189
|
-
: await auth[method]('person@example.com', {
|
|
1190
|
-
url: 'https://example.com'
|
|
1191
|
-
});
|
|
1192
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_NO_TOKEN_RETURNED.code);
|
|
1193
|
-
expect(generateEmailActionLink).not.toHaveBeenCalled();
|
|
1194
|
-
expect(mockedCreateSessionCookieEndpoint).not.toHaveBeenCalled();
|
|
1195
|
-
});
|
|
1196
|
-
describe('createSessionCookie', () => {
|
|
1197
|
-
it.each([
|
|
1198
|
-
undefined,
|
|
1199
|
-
null,
|
|
1200
|
-
3600000,
|
|
1201
|
-
{},
|
|
1202
|
-
{ expiresIn: '3600000' },
|
|
1203
|
-
{ expiresIn: NaN },
|
|
1204
|
-
{ expiresIn: Infinity },
|
|
1205
|
-
{ expiresIn: 299999 },
|
|
1206
|
-
{ expiresIn: 1209600001 }
|
|
1207
|
-
])('rejects invalid options %j before authentication', async (options) => {
|
|
1208
|
-
const result = await auth.createSessionCookie('id-token', options);
|
|
1209
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo
|
|
1210
|
-
.ADMIN_SESSION_COOKIE_DURATION_INVALID.code);
|
|
1211
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
1212
|
-
});
|
|
1213
|
-
it.each(['', null, 1])('rejects invalid ID token %s', async (idToken) => {
|
|
1214
|
-
const result = await auth.createSessionCookie(idToken, { expiresIn: 300000 });
|
|
1215
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_ID_TOKEN_INVALID.code);
|
|
1216
|
-
expect(mockedGetToken).not.toHaveBeenCalled();
|
|
1217
|
-
});
|
|
1218
|
-
it.each([300000, 1209600000])('accepts boundary lifetime %s and forwards tenant and fetch', async (expiresIn) => {
|
|
1219
|
-
const fetchFn = vi.fn();
|
|
1220
|
-
const admin = new FirebaseAdminAuth(serviceAccountKey, 'tenant', fetchFn);
|
|
1221
|
-
mockedGetToken.mockResolvedValue({
|
|
1222
|
-
data: mockGoogleTokenResponse,
|
|
1223
|
-
error: null
|
|
1224
|
-
});
|
|
1225
|
-
mockedCreateSessionCookieEndpoint.mockResolvedValue({
|
|
1226
|
-
data: 'cookie',
|
|
1227
|
-
error: null
|
|
1228
|
-
});
|
|
1229
|
-
expect(await admin.createSessionCookie('id-token', { expiresIn })).toEqual({ data: 'cookie', error: null });
|
|
1230
|
-
expect(mockedCreateSessionCookieEndpoint).toHaveBeenCalledWith('id-token', 'test-access-token', 'test-project', expiresIn, 'tenant', fetchFn);
|
|
1231
|
-
});
|
|
1232
|
-
it('returns an error for an empty cookie response', async () => {
|
|
1233
|
-
mockedGetToken.mockResolvedValue({
|
|
1234
|
-
data: mockGoogleTokenResponse,
|
|
1235
|
-
error: null
|
|
1236
|
-
});
|
|
1237
|
-
mockedCreateSessionCookieEndpoint.mockResolvedValue({
|
|
1238
|
-
data: null,
|
|
1239
|
-
error: null
|
|
1240
|
-
});
|
|
1241
|
-
const result = await auth.createSessionCookie('id-token', {
|
|
1242
|
-
expiresIn: 300000
|
|
1243
|
-
});
|
|
1244
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SESSION_COOKIE_CREATE_FAILED
|
|
1245
|
-
.code);
|
|
1246
|
-
});
|
|
1247
|
-
it('wraps unexpected endpoint failures', async () => {
|
|
1248
|
-
mockedGetToken.mockResolvedValue({
|
|
1249
|
-
data: mockGoogleTokenResponse,
|
|
1250
|
-
error: null
|
|
1251
|
-
});
|
|
1252
|
-
mockedCreateSessionCookieEndpoint.mockRejectedValue(new Error('network'));
|
|
1253
|
-
const result = await auth.createSessionCookie('id-token', {
|
|
1254
|
-
expiresIn: 300000
|
|
1255
|
-
});
|
|
1256
|
-
expect(result.error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SESSION_COOKIE_CREATE_FAILED
|
|
1257
|
-
.code);
|
|
1258
|
-
});
|
|
1259
|
-
it('returns error when getToken fails', async () => {
|
|
1260
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1261
|
-
data: null,
|
|
1262
|
-
error: new Error('unauthorized')
|
|
1263
|
-
});
|
|
1264
|
-
const result = await auth.createSessionCookie('id-token', {
|
|
1265
|
-
expiresIn: 3600000
|
|
1266
|
-
});
|
|
1267
|
-
expect(result.data).toBeNull();
|
|
1268
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1269
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED
|
|
1270
|
-
.code);
|
|
1271
|
-
});
|
|
1272
|
-
it('returns error when token is missing', async () => {
|
|
1273
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1274
|
-
data: null,
|
|
1275
|
-
error: new FirebaseEdgeError(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED)
|
|
1276
|
-
});
|
|
1277
|
-
const result = await auth.createSessionCookie('id-token', {
|
|
1278
|
-
expiresIn: 3600000
|
|
1279
|
-
});
|
|
1280
|
-
expect(result.data).toBeNull();
|
|
1281
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1282
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SERVICE_ACCOUNT_TOKEN_FAILED
|
|
1283
|
-
.code);
|
|
1284
|
-
});
|
|
1285
|
-
it('returns error when endpoint returns error', async () => {
|
|
1286
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1287
|
-
data: mockGoogleTokenResponse,
|
|
1288
|
-
error: null
|
|
1289
|
-
});
|
|
1290
|
-
const endpointError = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_INVALID_ARGUMENT);
|
|
1291
|
-
mockedCreateSessionCookieEndpoint.mockResolvedValueOnce({
|
|
1292
|
-
data: null,
|
|
1293
|
-
error: endpointError
|
|
1294
|
-
});
|
|
1295
|
-
const result = await auth.createSessionCookie('id-token', {
|
|
1296
|
-
expiresIn: 3600000
|
|
1297
|
-
});
|
|
1298
|
-
expect(mockedCreateSessionCookieEndpoint).toHaveBeenCalledWith('id-token', 'test-access-token', 'test-project', 3600000, undefined, undefined);
|
|
1299
|
-
expect(result.data).toBeNull();
|
|
1300
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1301
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SESSION_COOKIE_CREATE_FAILED
|
|
1302
|
-
.code);
|
|
1303
|
-
});
|
|
1304
|
-
it('returns session cookie data on success', async () => {
|
|
1305
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1306
|
-
data: mockGoogleTokenResponse,
|
|
1307
|
-
error: null
|
|
1308
|
-
});
|
|
1309
|
-
const cookieData = 'cookie-value';
|
|
1310
|
-
mockedCreateSessionCookieEndpoint.mockResolvedValueOnce({
|
|
1311
|
-
data: cookieData,
|
|
1312
|
-
error: null
|
|
1313
|
-
});
|
|
1314
|
-
const result = await auth.createSessionCookie('id-token', {
|
|
1315
|
-
expiresIn: 3600000
|
|
1316
|
-
});
|
|
1317
|
-
expect(result.data).toEqual(cookieData);
|
|
1318
|
-
expect(result.error).toBeNull();
|
|
1319
|
-
});
|
|
1320
|
-
});
|
|
1321
|
-
describe('verifySessionCookie', () => {
|
|
1322
|
-
it('rejects disabled users when revocation checking is enabled', async () => {
|
|
1323
|
-
mockedVerifySessionJWT.mockResolvedValue({
|
|
1324
|
-
data: mockFirebasePayload,
|
|
1325
|
-
error: null
|
|
1326
|
-
});
|
|
1327
|
-
vi.spyOn(auth, 'getUser').mockResolvedValue({
|
|
1328
|
-
data: { ...mockUserRecord, disabled: true },
|
|
1329
|
-
error: null
|
|
1330
|
-
});
|
|
1331
|
-
expect((await auth.verifySessionCookie('cookie', true)).error?.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_USER_DISABLED.code);
|
|
1332
|
-
});
|
|
1333
|
-
it.each([
|
|
1334
|
-
[
|
|
1335
|
-
'1001',
|
|
1336
|
-
FirebaseAdminAuthErrorInfo.ADMIN_SESSION_COOKIE_REVOKED.code
|
|
1337
|
-
],
|
|
1338
|
-
['1000', undefined],
|
|
1339
|
-
['999', undefined],
|
|
1340
|
-
[undefined, undefined]
|
|
1341
|
-
])('checks auth_time against validSince %s, not issuance time', async (validSince, expectedCode) => {
|
|
1342
|
-
mockedVerifySessionJWT.mockResolvedValue({
|
|
1343
|
-
data: { ...mockFirebasePayload, iat: 1500 },
|
|
1344
|
-
error: null
|
|
1345
|
-
});
|
|
1346
|
-
vi.spyOn(auth, 'getUser').mockResolvedValue({
|
|
1347
|
-
data: { ...mockUserRecord, validSince },
|
|
1348
|
-
error: null
|
|
1349
|
-
});
|
|
1350
|
-
const result = await auth.verifySessionCookie('cookie', true);
|
|
1351
|
-
expect(result.error?.code).toBe(expectedCode);
|
|
1352
|
-
expect(result.data === null).toBe(Boolean(expectedCode));
|
|
1353
|
-
});
|
|
1354
|
-
it('skips the user lookup by default', async () => {
|
|
1355
|
-
mockedVerifySessionJWT.mockResolvedValue({
|
|
1356
|
-
data: mockFirebasePayload,
|
|
1357
|
-
error: null
|
|
1358
|
-
});
|
|
1359
|
-
const lookup = vi.spyOn(auth, 'getUser');
|
|
1360
|
-
expect(await auth.verifySessionCookie('cookie')).toEqual({
|
|
1361
|
-
data: mockFirebasePayload,
|
|
1362
|
-
error: null
|
|
1363
|
-
});
|
|
1364
|
-
expect(lookup).not.toHaveBeenCalled();
|
|
1365
|
-
});
|
|
1366
|
-
it('returns error when verifySessionJWT fails', async () => {
|
|
1367
|
-
mockedVerifySessionJWT.mockResolvedValueOnce({
|
|
1368
|
-
data: null,
|
|
1369
|
-
error: new Error('bad session token')
|
|
1370
|
-
});
|
|
1371
|
-
const result = await auth.verifySessionCookie('session-cookie');
|
|
1372
|
-
expect(result.data).toBeNull();
|
|
1373
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1374
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SESSION_COOKIE_VERIFY_FAILED
|
|
1375
|
-
.code);
|
|
1376
|
-
});
|
|
1377
|
-
it('returns data when not checking revoked', async () => {
|
|
1378
|
-
mockedVerifySessionJWT.mockResolvedValueOnce({
|
|
1379
|
-
data: mockFirebasePayload,
|
|
1380
|
-
error: null
|
|
1381
|
-
});
|
|
1382
|
-
const result = await auth.verifySessionCookie('session-cookie', false);
|
|
1383
|
-
expect(result.data).toEqual(mockFirebasePayload);
|
|
1384
|
-
expect(result.error).toBeNull();
|
|
1385
|
-
});
|
|
1386
|
-
it('returns error when user lookup fails during revoked check', async () => {
|
|
1387
|
-
mockedVerifySessionJWT.mockResolvedValueOnce({
|
|
1388
|
-
data: mockFirebasePayload,
|
|
1389
|
-
error: null
|
|
1390
|
-
});
|
|
1391
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1392
|
-
data: mockGoogleTokenResponse,
|
|
1393
|
-
error: null
|
|
1394
|
-
});
|
|
1395
|
-
const error = new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_INTERNAL_ERROR);
|
|
1396
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
1397
|
-
data: null,
|
|
1398
|
-
error
|
|
1399
|
-
});
|
|
1400
|
-
const result = await auth.verifySessionCookie('session-cookie', true);
|
|
1401
|
-
expect(result.data).toBeNull();
|
|
1402
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1403
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_USER_LOOKUP_FAILED.code);
|
|
1404
|
-
});
|
|
1405
|
-
it('returns ERR_NO_USER when user is null during revoked check', async () => {
|
|
1406
|
-
mockedVerifySessionJWT.mockResolvedValueOnce({
|
|
1407
|
-
data: mockFirebasePayload,
|
|
1408
|
-
error: null
|
|
1409
|
-
});
|
|
1410
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1411
|
-
data: mockGoogleTokenResponse,
|
|
1412
|
-
error: null
|
|
1413
|
-
});
|
|
1414
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
1415
|
-
data: null,
|
|
1416
|
-
error: null
|
|
1417
|
-
});
|
|
1418
|
-
const result = await auth.verifySessionCookie('session-cookie', true);
|
|
1419
|
-
expect(result.data).toBeNull();
|
|
1420
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1421
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_USER_RECORD_NOT_FOUND.code);
|
|
1422
|
-
});
|
|
1423
|
-
it('returns decoded data when revoked check passes', async () => {
|
|
1424
|
-
mockedVerifySessionJWT.mockResolvedValueOnce({
|
|
1425
|
-
data: mockFirebasePayload,
|
|
1426
|
-
error: null
|
|
1427
|
-
});
|
|
1428
|
-
mockedGetToken.mockResolvedValueOnce({
|
|
1429
|
-
data: mockGoogleTokenResponse,
|
|
1430
|
-
error: null
|
|
1431
|
-
});
|
|
1432
|
-
mockedGetAccountInfo.mockResolvedValueOnce({
|
|
1433
|
-
data: mockUserRecord,
|
|
1434
|
-
error: null
|
|
1435
|
-
});
|
|
1436
|
-
const result = await auth.verifySessionCookie('session-cookie', true);
|
|
1437
|
-
expect(result.data).toEqual(mockFirebasePayload);
|
|
1438
|
-
expect(result.error).toBeNull();
|
|
1439
|
-
});
|
|
1440
|
-
it('returns error when no decoded data returned', async () => {
|
|
1441
|
-
mockedVerifySessionJWT.mockResolvedValueOnce({
|
|
1442
|
-
data: null,
|
|
1443
|
-
error: new FirebaseEdgeError(FirebaseEndpointErrorInfo.ENDPOINT_INVALID_SESSION_COOKIE)
|
|
1444
|
-
});
|
|
1445
|
-
const result = await auth.verifySessionCookie('session-cookie');
|
|
1446
|
-
expect(result.data).toBeNull();
|
|
1447
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1448
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_SESSION_COOKIE_VERIFY_FAILED
|
|
1449
|
-
.code);
|
|
1450
|
-
});
|
|
1451
|
-
it('returns error when tenant ID does not match', async () => {
|
|
1452
|
-
const mockPayloadWithDifferentTenant = {
|
|
1453
|
-
...mockFirebasePayload,
|
|
1454
|
-
firebase: {
|
|
1455
|
-
...mockFirebasePayload.firebase,
|
|
1456
|
-
tenant: 'wrong-tenant-id'
|
|
1457
|
-
}
|
|
1458
|
-
};
|
|
1459
|
-
mockedVerifySessionJWT.mockResolvedValueOnce({
|
|
1460
|
-
data: mockPayloadWithDifferentTenant,
|
|
1461
|
-
error: null
|
|
1462
|
-
});
|
|
1463
|
-
const result = await authWithTenant.verifySessionCookie('session-cookie');
|
|
1464
|
-
expect(result.data).toBeNull();
|
|
1465
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1466
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_TENANT_ID_INVALID.code);
|
|
1467
|
-
});
|
|
1468
|
-
it('returns success when tenant ID matches', async () => {
|
|
1469
|
-
const mockPayloadWithCorrectTenant = {
|
|
1470
|
-
...mockFirebasePayload,
|
|
1471
|
-
firebase: {
|
|
1472
|
-
...mockFirebasePayload.firebase,
|
|
1473
|
-
tenant: 'test-tenant-id'
|
|
1474
|
-
}
|
|
1475
|
-
};
|
|
1476
|
-
mockedVerifySessionJWT.mockResolvedValueOnce({
|
|
1477
|
-
data: mockPayloadWithCorrectTenant,
|
|
1478
|
-
error: null
|
|
1479
|
-
});
|
|
1480
|
-
const result = await authWithTenant.verifySessionCookie('session-cookie', false);
|
|
1481
|
-
expect(result.data).toEqual(mockPayloadWithCorrectTenant);
|
|
1482
|
-
expect(result.error).toBeNull();
|
|
1483
|
-
});
|
|
1484
|
-
});
|
|
1485
|
-
describe('createCustomToken', () => {
|
|
1486
|
-
it('returns error when signJWTCustomToken fails', async () => {
|
|
1487
|
-
mockedSignJWTCustomToken.mockResolvedValueOnce({
|
|
1488
|
-
data: null,
|
|
1489
|
-
error: new Error('sign failed')
|
|
1490
|
-
});
|
|
1491
|
-
const result = await auth.createCustomToken('uid-1');
|
|
1492
|
-
expect(mockedSignJWTCustomToken).toHaveBeenCalledWith('uid-1', serviceAccountKey, {});
|
|
1493
|
-
expect(result.data).toBeNull();
|
|
1494
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1495
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_CUSTOM_TOKEN_CREATE_FAILED.code);
|
|
1496
|
-
});
|
|
1497
|
-
it('returns error when no data is returned', async () => {
|
|
1498
|
-
mockedSignJWTCustomToken.mockResolvedValueOnce({
|
|
1499
|
-
data: null,
|
|
1500
|
-
error: new FirebaseEdgeError(JWTErrorInfo.JWT_UNKNOWN_SIGNING_ERROR)
|
|
1501
|
-
});
|
|
1502
|
-
const result = await auth.createCustomToken('uid-1');
|
|
1503
|
-
expect(result.data).toBeNull();
|
|
1504
|
-
expect(result.error).toBeInstanceOf(FirebaseEdgeError);
|
|
1505
|
-
expect(result.error.code).toBe(FirebaseAdminAuthErrorInfo.ADMIN_CUSTOM_TOKEN_CREATE_FAILED.code);
|
|
1506
|
-
});
|
|
1507
|
-
it('returns token when successful', async () => {
|
|
1508
|
-
mockedSignJWTCustomToken.mockResolvedValueOnce({
|
|
1509
|
-
data: 'custom-token',
|
|
1510
|
-
error: null
|
|
1511
|
-
});
|
|
1512
|
-
const claims = { role: 'admin' };
|
|
1513
|
-
const result = await auth.createCustomToken('uid-1', claims);
|
|
1514
|
-
expect(mockedSignJWTCustomToken).toHaveBeenCalledWith('uid-1', serviceAccountKey, claims);
|
|
1515
|
-
expect(result.data).toBe('custom-token');
|
|
1516
|
-
expect(result.error).toBeNull();
|
|
1517
|
-
});
|
|
1518
|
-
it('includes tenant_id claim when tenant is specified', async () => {
|
|
1519
|
-
mockedSignJWTCustomToken.mockResolvedValueOnce({
|
|
1520
|
-
data: 'custom-token-with-tenant',
|
|
1521
|
-
error: null
|
|
1522
|
-
});
|
|
1523
|
-
const claims = { role: 'admin' };
|
|
1524
|
-
const result = await authWithTenant.createCustomToken('uid-1', claims);
|
|
1525
|
-
expect(mockedSignJWTCustomToken).toHaveBeenCalledWith('uid-1', serviceAccountKey, { ...claims, tenant_id: 'test-tenant-id' });
|
|
1526
|
-
expect(result.data).toBe('custom-token-with-tenant');
|
|
1527
|
-
expect(result.error).toBeNull();
|
|
1528
|
-
});
|
|
1529
|
-
it('does not add tenant_id when no tenant specified', async () => {
|
|
1530
|
-
mockedSignJWTCustomToken.mockResolvedValueOnce({
|
|
1531
|
-
data: 'custom-token',
|
|
1532
|
-
error: null
|
|
1533
|
-
});
|
|
1534
|
-
const claims = { role: 'user' };
|
|
1535
|
-
const result = await auth.createCustomToken('uid-1', claims);
|
|
1536
|
-
expect(mockedSignJWTCustomToken).toHaveBeenCalledWith('uid-1', serviceAccountKey, claims);
|
|
1537
|
-
expect(result.data).toBe('custom-token');
|
|
1538
|
-
expect(result.error).toBeNull();
|
|
1539
|
-
});
|
|
1540
|
-
});
|
|
1541
|
-
});
|
|
1542
|
-
//# sourceMappingURL=firebase-admin-auth.test.js.map
|