firebase / firebase-admin-go

Firebase Admin Go SDK
Apache License 2.0
1.13k stars 242 forks source link

Allow emulator CustomTokens to be verified #463

Closed steeling closed 2 years ago

steeling commented 2 years ago

Right now, a CustomToken fails verification due to the aud claim not being set appropriately https://github.com/firebase/firebase-admin-go/blob/master/auth/auth.go#L35 https://github.com/firebase/firebase-admin-go/blob/master/auth/token_verifier.go#L280

This prevents me from generating a custom token to use in E2E tests. There's no other clients API for go, so my alternative is to issue HTTP requests directly, which is a lot more cumbersome, or to conditionally verify the token based on the environment which I'd prefer not to do.

These tokens aren't signed, so why not set the aud? Or add a "-test" suffix to the aud

google-oss-bot commented 2 years ago

I found a few problems with this issue:

will-osborne commented 2 years ago

I am also experiencing the same issue with the same usecase.

yuchenshi commented 2 years ago

Hi there, can someone share a sample stack trace and a sample token where aud isn't set properly? The Auth Emulator seems to set aud here but there may be some other issue we are unaware of. https://github.com/firebase/firebase-tools/blob/81ec80303ced030d12c2c444da698686b17fa483/src/emulator/auth/operations.ts#L2246

will-osborne commented 2 years ago

Hi there, can someone share a sample stack trace and a sample token where aud isn't set properly? The Auth Emulator seems to set aud here but there may be some other issue we are unaware of. https://github.com/firebase/firebase-tools/blob/81ec80303ced030d12c2c444da698686b17fa483/src/emulator/auth/operations.ts#L2246

Hi @yuchenshi,

Error #01: invalid token: ID token has invalid 'aud' (audience) claim; expected "development-341409" but got "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"; make sure the ID token comes from the same Firebase project as the credential used to authenticate this SDK; see https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve a valid ID token

As you can see the VerifyIDToken func expects the audience to be the projectID of the emulator, wheras the CustomToken function sets the audience of the token to the IdentityToolkit URI.

My workaround for this was to use the golang-jwt pkg to manually create a JWT with the following claims (where user var is a UserRecord of a test user that exists in firebase auth emulator)

jwtClaims := jwt.MapClaims{
                "alg": "RS256",
                "iss": "https://securetoken.google.com/development-341409",
                "sub": user.UID,
                "aud": "development-341409",
                "iat": time.Now().Unix(),
                "exp": time.Now().Add(time.Hour).Unix(),
                "uid": user.UID,
            }
yuchenshi commented 2 years ago

@will-osborne Thanks for sharing your use case. Just to clarify, a Custom Token is not a stand-in for a Firebase Auth ID token, but a different kind of credential that can be used to exchange for an ID token. The exchange process involves calling a backend API (or using the SDK, as documented in the link above), which can be either Firebase Auth or its emulator. The API call will return an ID token (via REST or SDK).

As such, the Custom Token has a different aud claim to signify that is intended for consumption of the backend (therefore, the googleapis URL), not your application / server / script. Therefore, the Verify call will reject Custom Tokens, since they are expecting ID tokens (the exchange result). Now, I understand that you'd like a stand-in for an ID token during your tests, and there are different approaches to that.

  1. Create a JWT manually, as suggested by @will-osborne in the code snippet. Such a JWT will not be signed by the Firebase Auth backend private keys (since those are only available in the real production backend), but you can make it so that it has almost the same shape (e.g. claims including aud).
  2. Use the Auth Emulator, which will essentially act a "fake" for the Firebase Auth component in your e2e / integration test. This allows you to "sign in" to the Auth Emulator and get an unsigned ID token, and then validate it using the Go Admin SDK.
    • The ID token will not be signed either, but the Go Admin SDK has special behavior when FIREBASE_AUTH_EMULATOR_HOST to make it pass verification.
    • The Firebase team strives to keep the Auth Emulator behavior in sync with production, including the token shape (e.g. claims).
    • This is the recommended path forward, especially if you test other features involving Auth (e.g. fetching user profiles).
    • For headless e2e tests, you can sign in using a Custom Token using Client SDKs or REST.
  3. Conditionally bypass the call to Go Admin SDK (e.g. Verify) in your code. This essentially narrows the scope of your test to exclude the Firebase Auth-related parts. You may want to use request parameters / env vars or other ways to trigger this behavior (and provide stand-in data for the user fields).
    • You'll definitely want to make sure such test behavior cannot be triggered in production for security reasons.

As one can tell, each has a different scope and focus, and one can choose for themselves based on the exact goal and need of their tests. At Firebase, we believe "fakes are better than mocks" and therefore we're providing a full Emulator Suite, including the Auth Emulator, which does issue fake ID tokens but also more than that (Option 2). We hope this helps you write better tests.

I'm going to close this issue since Custom Tokens aren't the solution to your use case, but please let us know if none of the options above work for you and let's see how we can bridge the gap.