endpoints/getting-started/client/main.go (92 lines of code) (raw):

// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Command client performs authenticated requests against an Endpoints API server. package main import ( "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "flag" "fmt" "io" "log" "net/http" "os" "time" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jws" ) var ( url = flag.String("url", "", "The request url. Required.") audience = flag.String("audience", "", "The audience for the JWT. equired") serviceAccountFile = flag.String("service-account-file", "", "Path to service account JSON file. Required.") serviceAccountEmail = flag.String("service-account-email", "", "Path email associated with the service account. Required.") ) func main() { flag.Parse() if *audience == "" || *url == "" || *serviceAccountFile == "" || *serviceAccountEmail == "" { fmt.Println("requires: --url, --audience, --service-account-file, --service-account-email") os.Exit(1) } jwt, err := generateJWT(*serviceAccountFile, *serviceAccountEmail, *audience, 3600) if err != nil { log.Fatal(err) } resp, err := makeJWTRequest(jwt, *url) if err != nil { log.Fatal(err) } fmt.Printf("%s Response: %s", *url, resp) } // [START endpoints_generate_jwt_sa] // generateJWT creates a signed JSON Web Token using a Google API Service Account. func generateJWT(saKeyfile, saEmail, audience string, expiryLength int64) (string, error) { now := time.Now().Unix() // Build the JWT payload. jwt := &jws.ClaimSet{ Iat: now, // expires after 'expiryLength' seconds. Exp: now + expiryLength, // Iss must match 'issuer' in the security configuration in your // swagger spec (e.g. service account email). It can be any string. Iss: saEmail, // Aud must be either your Endpoints service name, or match the value // specified as the 'x-google-audience' in the OpenAPI document. Aud: audience, // Sub and Email should match the service account's email address. Sub: saEmail, PrivateClaims: map[string]interface{}{"email": saEmail}, } jwsHeader := &jws.Header{ Algorithm: "RS256", Typ: "JWT", } // Extract the RSA private key from the service account keyfile. sa, err := os.ReadFile(saKeyfile) if err != nil { return "", fmt.Errorf("could not read service account file: %w", err) } conf, err := google.JWTConfigFromJSON(sa) if err != nil { return "", fmt.Errorf("could not parse service account JSON: %w", err) } block, _ := pem.Decode(conf.PrivateKey) parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return "", fmt.Errorf("private key parse error: %w", err) } rsaKey, ok := parsedKey.(*rsa.PrivateKey) // Sign the JWT with the service account's private key. if !ok { return "", errors.New("private key failed rsa.PrivateKey type assertion") } return jws.Encode(jwsHeader, jwt, rsaKey) } // [END endpoints_generate_jwt_sa] // [START endpoints_jwt_request] // makeJWTRequest sends an authorized request to your deployed endpoint. func makeJWTRequest(signedJWT, url string) (string, error) { client := &http.Client{ Timeout: 10 * time.Second, } req, err := http.NewRequest("GET", url, nil) if err != nil { return "", fmt.Errorf("failed to create HTTP request: %w", err) } req.Header.Add("Authorization", "Bearer "+signedJWT) req.Header.Add("content-type", "application/json") response, err := client.Do(req) if err != nil { return "", fmt.Errorf("HTTP request failed: %w", err) } defer response.Body.Close() responseData, err := io.ReadAll(response.Body) if err != nil { return "", fmt.Errorf("failed to parse HTTP response: %w", err) } return string(responseData), nil } // [END endpoints_jwt_request]