Google Cloud Endpoints + Firebase Auth: method_inf

2020-03-26 03:44发布

So I am running the sample code provided by Google:

package com.neat.backend;
/**
 * An endpoint class we are exposing
 */
@Api(
        name = "myApi",
        version = "v1",
        namespace = @ApiNamespace(
                ownerDomain = "backend.neat.com",
                ownerName = "backend.neat.com",
                packagePath = ""
        ),
        issuers = {
        @ApiIssuer(
                name = "firebase",
                issuer = "https://securetoken.google.com/" + PROJECT_ID,
                jwksUri = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com")
})
public class MyEndpoint {

    @ApiMethod(
            path = "firebase_user",
            httpMethod = ApiMethod.HttpMethod.GET,
            authenticators = {EspAuthenticator.class},
            issuerAudiences = {@ApiIssuerAudience(name = "firebase", audiences = {PROJECT_ID})}
    )
    public Email getUserEmailFirebase(User user) throws UnauthorizedException {
        if (user == null) {
            throw new UnauthorizedException("Invalid credentials");
        }

        Email response = new Email(user.getEmail());
        return response;
    }

}

I am getting a Firebase token from my Android client and try to send it to the backend by:

curl -H "Authorization: Bearer FIREBASE_JWT_TOKEN" \
     -X GET \
     http://localhost:8080/_ah/api/echo/v1/firebase_user

The error I see in the logs is the following:

[INFO] java.lang.IllegalStateException: method_info is not set in the request
[INFO]  at com.google.api.server.spi.auth.EspAuthenticator.authenticate(EspAuthenticator.java:67)
[INFO]  at com.google.api.server.spi.request.Auth.authenticate(Auth.java:100)
[INFO]  at com.google.api.server.spi.request.ServletRequestParamReader.getUser(ServletRequestParamReader.java:191)
[INFO]  at com.google.api.server.spi.request.ServletRequestParamReader.deserializeParams(ServletRequestParamReader.java:136)
[INFO]  at com.google.api.server.spi.request.RestServletRequestParamReader.read(RestServletRequestParamReader.java:123)
[INFO]  at com.google.api.server.spi.SystemService.invokeServiceMethod(SystemService.java:350)
[INFO]  at com.google.api.server.spi.handlers.EndpointsMethodHandler$RestHandler.handle(EndpointsMethodHandler.java:114)
[INFO]  at com.google.api.server.spi.handlers.EndpointsMethodHandler$RestHandler.handle(EndpointsMethodHandler.java:102)
[INFO]  at com.google.api.server.spi.dispatcher.PathDispatcher.dispatch(PathDispatcher.java:49)
[INFO]  at com.google.api.server.spi.EndpointsServlet.service(EndpointsServlet.java:71)
[INFO]  at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

I have tried deploying the exact same code to App Engine and it works perfectly. I have tried debugging EspAuthenticator and it seems that it is expecting the Servlet filters to inject some attributes in the request.

4条回答
做自己的国王
2楼-- · 2020-03-26 04:00

generat and deploy the OpenAPI configuration file

$ mvn clean package endpoints-framework:openApiDocs -DskipTests
$ gcloud endpoints services deploy target/openapi-docs/openapi.json
$ mvn appengine:run 
查看更多
别忘想泡老子
3楼-- · 2020-03-26 04:11

@Kevendra's answer highlights that this issue can be caused if an openapi.json file is missing a reference to the endpoint API method. Firebase may be using this to reference and discover the API method.

From the Google OpenAPI Overview:

Basic structure of an OpenAPI document:

An OpenAPI document describes the surface of your REST API, and defines information such as:

The name and description of the API. The individual endpoints (paths) in the API. How the callers are authenticated.

Follow these steps to regenerate and deploy the openapi.json file:


generate:

$ mvn clean package endpoints-framework:openApiDocs -DskipTests
  • mvn clean - run a Maven goal to clean your project. package - compile and package it
  • endpoints-framework:openApiDocs generate OpenAPI documents. This will generate the openapi.json file at the location: target/openapi-docs/openapi.json- see generating and deploying an api configuration.
  • -DskipTests skips running any tests, to avoid any test failure due to the openapi.json not yet being generated

deploy:

(As a precaution you can first validate the project ID returned from the following command to make sure that the service isn't created in the wrong project - gcloud config list project)

  $ gcloud endpoints services deploy target/openapi-docs/openapi.json

Deploys the openapi.json file from its generated location (in the 'generate' step above). See the Google docs on Deploying the OpenAPI document

查看更多
Anthone
4楼-- · 2020-03-26 04:15

It took me a while and a bit of debugging to realize that the filter that is supposed to inject method_info was not being fired.

I could fix it by modifying the mapping in web.xml adding the following dispatcher tags:

<filter-mapping>
    <filter-name>endpoints-api-configuration</filter-name>
    <servlet-name>EndpointsServlet</servlet-name>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>
查看更多
来,给爷笑一个
5楼-- · 2020-03-26 04:23

I got the same error message, and eventually tracked it down to making a request with a trailing / in the URL where my API did not specify one. The trailing slash only causes an error for calls that provide an authorization token.

Apparently ControlFilter (and therefore also GoogleAppEngineControlFilter) did not recognize it as a valid endpoint and therefore did not bother attaching method_info to the request. But then EndpointsServlet thought it was valid and tried to authenticate without all the needed info!

The fix was easy: remove the trailing slash from the URL in my request. Tracking down the problem, however, was not! I see this was not your problem, but maybe this answer will help someone else.

查看更多
登录 后发表回答