Skip to content

Commit

Permalink
Support x-goog-api-key in Auth Emulator. Fix #5249.
Browse files Browse the repository at this point in the history
  • Loading branch information
yuchenshi committed Nov 22, 2022
1 parent 8113804 commit 0523884
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 9 deletions.
28 changes: 20 additions & 8 deletions src/emulator/auth/server.ts
Expand Up @@ -166,14 +166,25 @@ export async function createApp(
);

const apiKeyAuthenticator: PromiseAuthenticator = (ctx, info) => {
if (info.in !== "query") {
throw new Error('apiKey must be defined as in: "query" in API spec.');
}
if (!info.name) {
throw new Error("apiKey param name is undefined in API spec.");
throw new Error("apiKey param/header name is undefined in API spec.");
}

let key: string | undefined;
const req = ctx.req as express.Request;
switch (info.in) {
case "header":
key = req.get(info.name);
break;
case "query": {
const q = req.query[info.name];
key = typeof q === "string" ? q : undefined;
break;
}
default:
throw new Error('apiKey must be defined as in: "query" or "header" in API spec.');
}
const key = (ctx.req as express.Request).query[info.name];
if (typeof key === "string" && key.length > 0) {
if (key) {
return { type: "success", user: getProjectIdByApiKey(key) };
} else {
return undefined;
Expand Down Expand Up @@ -218,7 +229,8 @@ export async function createApp(
const apis = await exegesisExpress.middleware(specForRouter(), {
controllers: { auth: toExegesisController(authOperations, getProjectStateById) },
authenticators: {
apiKey: apiKeyAuthenticator,
apiKeyQuery: apiKeyAuthenticator,
apiKeyHeader: apiKeyAuthenticator,
Oauth2: oauth2Authenticator,
},
autoHandleHttpErrors(err) {
Expand Down Expand Up @@ -305,7 +317,7 @@ export async function createApp(
if (ctx.res.statusCode === 401) {
// Normalize unauthenticated responses to match production.
const requirements = (ctx.api.operationObject as OperationObject).security;
if (requirements?.some((req) => req.apiKey)) {
if (requirements?.some((req) => req.apiKeyQuery || req.apiKeyHeader)) {
throw new PermissionDeniedError("The request is missing a valid API key.");
} else {
throw new UnauthenticatedError(
Expand Down
28 changes: 27 additions & 1 deletion src/test/emulators/auth/rest.spec.ts
Expand Up @@ -93,6 +93,28 @@ describeAuthEmulator("authentication", ({ authApi }) => {
});
});

it("should accept API key as a query parameter", async () => {
await authApi()
.post("/identitytoolkit.googleapis.com/v1/accounts:signUp")
.query({ key: "fake-api-key" })
.send({})
.then((res) => {
expectStatusCode(200, res);
expect(res.body).not.to.have.property("error");
});
});

it("should accept API key in HTTP Header x-goog-api-key", async () => {
await authApi()
.post("/identitytoolkit.googleapis.com/v1/accounts:signUp")
.set("x-goog-api-key", "fake-api-key")
.send({})
.then((res) => {
expectStatusCode(200, res);
expect(res.body).not.to.have.property("error");
});
});

it("should ignore non-Bearer Authorization headers", async () => {
await authApi()
.post("/identitytoolkit.googleapis.com/v1/accounts:signUp")
Expand Down Expand Up @@ -143,7 +165,11 @@ describeAuthEmulator("authentication", ({ authApi }) => {
.post("/identitytoolkit.googleapis.com/v1/accounts:signUp")
// This authenticates as owner of the default projectId. The exact value
// and expiry don't matter -- the Emulator only checks for the format.
.set("Authorization", "Bearer ya29.AHES6ZRVmB7fkLtd1XTmq6mo0S1wqZZi3-Lh_s-6Uw7p8vtgSwg")
.set(
"Authorization",
// Not an actual token. Breaking it down to avoid linter false positives.
"Bearer ya" + "29.AHES0ZZZZZ0fff" + "ff0XXXX0mmmm0wwwww0-LL_l-0bb0b0bbbbbb"
)
.send({
// This field requires OAuth 2 and should work correctly.
targetProjectId: "example2",
Expand Down

0 comments on commit 0523884

Please sign in to comment.