GCP API 게이트웨이: Firebase 인증

23615 단어 firebasegogooglecloud

Google Cloud Platform API 게이트웨이



github 저장소 --> here

API 게이트웨이란 무엇입니까?



documentation에 따라 Api 게이트웨이는 서버리스 워크로드를 위한 완전 관리형 게이트웨이입니다.

그래서 정말 하루가 끝나면 서버리스 API를 위한 서버리스 게이트웨이로 귀결됩니다. 서버리스가 너무 많습니다.

API 게이트웨이는 무엇을 합니까?



API 게이트웨이는 최종 사용자와 서비스 사이의 중개자 역할을 합니다. OpenAPI 사양에 따라 서비스를 설명합니다.
사양을 API 게이트웨이 센터에 업로드한 다음 마지막으로 사양을 게이트웨이에 배포합니다. API Gateway는 모니터링, 로깅 및 인증과 같은 유틸리티 제품군도 제공합니다.

API 게이트웨이는 언제 사용해야 합니까?



서버리스 GCP 에코시스템(Cloud Functions, Cloud Run, App Engine)을 사용하려는 경우 API 게이트웨이는 이러한 서버리스 제품에 무료입니다.

API 게이트웨이를 사용해야 하는 이유는 무엇입니까?



대답해야 할 가장 중요한 질문은 "왜"입니다. 왜 API Gateway를 사용해야 합니까?

보안 - 핵심 애플리케이션은 GCP IAM에 의해 배포 및 보호될 수 있습니다. 이렇게 하면 서비스와의 직접적인 상호 작용만 api 게이트웨이를 통해 수행됩니다.

외부화된 구성 - 애플리케이션 컨텍스트 외부에 있는 애플리케이션 인증, 서비스 URL 매핑 및 API 문서를 관리하는 외부 방법이 있습니다.

적은 코드 - JWT/Api 키 유효성 검사에 대해 애플리케이션 자체에서 걱정할 필요가 없습니다. JWT/Api 키가 게이트웨이 계층에서 처리되고 결과가 애플리케이션으로 전달되기 때문입니다. 작성하는 코드가 적을수록 버그가 줄어듭니다 😬

관찰 가능성 - 모든 성능 메트릭은 요청 대기 시간, 오류율, 초당 요청 수 등과 같은 모든 KPI가 포함된 보기 쉬운 단일 대시보드로 롤업됩니다.

Firebase 인증으로 Cloud Run 서비스를 보호합니다.



먼저 API를 이해하기 위해 openapi 사양 파일을 살펴보겠습니다. 사용자 이름을 "인사하는"형식으로 되풀이하는 단일 끝점/greet으로 매우 간단합니다.
openapi2-run.yaml
swagger: '2.0'
info:
  # Title of the api gateway
  title: gateway
  description: Sample API on API Gateway with a Cloud Run backend
  version: 1.0.0
schemes:
  - https
produces:
  - application/json
# The cloud run service url, this could also be defined per path as well in case you have multiple cloud run services that
# make up a single gateway
x-google-backend:
  address: "YOUR-CLOUD_RUN-URL"
securityDefinitions:
  firebase:
    authorizationUrl: ""
    flow: "implicit"
    type: "oauth2"
    # Replace YOUR-PROJECT-ID with your project ID
    x-google-issuer: "https://securetoken.google.com/YOUR-PROJECT-ID"
    x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected]"
    x-google-audiences: "YOUR-PROJECT-ID"
paths:
  /greet:
    get:
      summary: Greets the user
      operationId: greet
      # define that our service uses the firebase security definition
      security:
        - firebase: [ ]
      responses:
        '200':
          description: A successful response
          schema:
            type: object
            # our object look like `{name: ""}`
            properties:
              name:
                type: string
                description: The users name

이제 api의 계약을 이행할 코드를 살펴보겠습니다.
cmd/routes.go
func (s *server) routes() {
    s.router.HandleFunc("/greet", s.handleAuth(s.handleGreeting()))
}

//handleGreeting will fetch the UserInfo struct that is stored in context from our auth middleware and use that to greet the person that called our api
func (s *server) handleGreeting() http.HandlerFunc {

    type person struct {
        Name string `json:"name"`
    }

    return func(writer http.ResponseWriter, request *http.Request) {

        writer.Header().Set("Content-Type", "application/json")

        // fetch the token user object that is stored in context
        userObj := request.Context().Value(gatewayUserContext).(UserInfo)

        // greet the user 👋
        p := person{Name: fmt.Sprintf("Hello 👋 %s", userObj.Name)}
        decoder := json.NewEncoder(writer)

        if err := decoder.Encode(p); err != nil {
            http.Error(writer, err.Error(), http.StatusInternalServerError)
        }
    }
}

그리고 Auth 미들웨어에 대한 빠른 검토를 위해.
cmd/auth.go
const gatewayUserInfoHeader = "X-Apigateway-Api-Userinfo"
const gatewayUserContext = "GATEWAY_USER"

type UserInfo struct {
    Name          string   `json:"name"`
    Picture       string   `json:"picture"`
    Iss           string   `json:"iss"`
    Aud           string   `json:"aud"`
    AuthTime      int      `json:"auth_time"`
    UserID        string   `json:"user_id"`
    Sub           string   `json:"sub"`
    Iat           int      `json:"iat"`
    Exp           int      `json:"exp"`
    Email         string   `json:"email"`
    EmailVerified bool     `json:"email_verified"`
    Firebase      Firebase `json:"firebase"`
}
type Identities struct {
    GoogleCom []string `json:"google.com"`
    Email     []string `json:"email"`
}
type Firebase struct {
    Identities     Identities `json:"identities"`
    SignInProvider string     `json:"sign_in_provider"`
}

// handleAuth is a piece of middleware that will parse the gatewayUserInfoHeader from the request and add the UserInfo to the request context
func (s *server) handleAuth(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {

        encodedUser := r.Header.Get(gatewayUserInfoHeader)
        if encodedUser == "" {
            http.Error(w, "User Not Available", http.StatusForbidden)
            return
        }
        decodedBytes, err := base64.RawURLEncoding.DecodeString(encodedUser)
        if err != nil {
            http.Error(w, "Invalid UserInfo", http.StatusForbidden)
            return
        }
        decoder := json.NewDecoder(bytes.NewReader(decodedBytes))
        var userToken UserInfo
        err = decoder.Decode(&userToken)
        if err != nil {
            http.Error(w, "Invalid UserInfo", http.StatusForbidden)
            return
        }

        ctx := context.WithValue(r.Context(), gatewayUserContext, userToken)
        h.ServeHTTP(w, r.WithContext(ctx))

    }
}

github repo에서 애플리케이션에 대한 나머지 코드를 볼 수 있습니다.

API 활성화 및 서비스 계정 생성



프로젝트에서 다음 API를 활성화했는지 확인하십시오.

gcloud services enable apigateway.googleapis.com
gcloud services enable servicemanagement.googleapis.com
gcloud services enable servicecontrol.googleapis.com

API 게이트웨이 서비스 계정을 만들 수 있습니다.

gcloud iam service-accounts create api-gateway

gcloud projects add-iam-policy-binding YOUR_PROJECT_ID --member "serviceAccount:api-gateway@YOUR_PROJECT_ID.iam.gserviceaccount.com" --role "roles/run.invoker"
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID --member "serviceAccount:api-gateway@YOUR_PROJECT_ID.iam.gserviceaccount.com" --role "roles/iam.serviceAccountUser"

빌드 및 배포



클라우드 빌드를 사용하여 모든 것을 출시할 예정이므로 컨테이너를 빌드하고 배포하도록 합시다. gcloud builds submitcloudbuild.yaml
steps:

  # Run the docker build
  - name: 'gcr.io/cloud-builders/docker'
    args: [ 'build', '-t', 'gcr.io/$PROJECT_ID/greeter', '.' ]

  # push the docker image to the private GCR registry
  - name: 'gcr.io/cloud-builders/docker'
    args: [ 'push', 'gcr.io/$PROJECT_ID/greeter' ]

  # deploy to cloud run
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    entrypoint: gcloud
    args: [ 'run', 'deploy', 'greeter', '--image', 'gcr.io/$PROJECT_ID/greeter', '--region', 'us-central1', '--platform', 'managed', '--no-allow-unauthenticated' ]

images:
  - 'gcr.io/$PROJECT_ID/greeter'


이제 클라우드 서비스가 배포되었으므로 클라우드 실행 URLgcloud run services describe greeter --format 'value(status.url)'만 가져오면 됩니다.
보안을 확인하기 위해 엔드포인트curlcurl $(gcloud run services describe greeter --format 'value(status.url)') 시도할 수 있으며 403을 얻어야 합니다.

다음 단계는 엔드포인트에서 해당 URL을 가져와 x-google-backend의 주소로 API 사양에 연결하는 것입니다.

# The cloud run service url, this could also be defined per path as well in case you have multiple cloud run services that
# make up a single gateway
x-google-backend:
  address: "YOUR-CLOUD_RUN-URL"

이제 우리는 홈 스트레치로 전환하고 있습니다. 게이트웨이/구성을 배포하기만 하면 됩니다.


# create api config
gcloud beta api-gateway api-configs create echoconf \
  --api=gateway --openapi-spec=openapi2-run.yaml \
  --backend-auth-service-account=api-gateway@YOUR_PROJECT_ID.iam.gserviceaccount.com


# create gateway with config
gcloud beta api-gateway gateways create gateway \
  --api=gateway --api-config=echoconf \
  --location=us-central1


#get hostname from gateway
gcloud beta api-gateway gateways describe gateway \
  --location=us-central1 --format 'value(defaultHostname)'

이제 게이트웨이 끝점을 curl 다음과 같이 하면

curl "https://$(gcloud beta api-gateway gateways describe gateway --location=us-central1 --format 'value(defaultHostname)')/greet"

요청에 Firebase ID 토큰을 첨부하지 않았기 때문에 401이 표시됩니다.

그러나 요청에 토큰을 access_token의 쿼리 매개변수 또는 전달자 토큰이 있는 Authorization 헤더로 첨부하면

curl "https://$(gcloud beta api-gateway gateways describe gateway --location=us-central1 --format 'value(defaultHostname)')/greet?access_token=ACCESS_TOKEN"

우리는 우리의 인사 메시지를 돌려받을 것입니다! {
"name": "Hello 👋 Alex Mammay"
}

좋은 웹페이지 즐겨찾기