1/* 2 * 3 * Copyright 2015 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19// Package oauth implements gRPC credentials using OAuth. 20package oauth 21 22import ( 23 "fmt" 24 "io/ioutil" 25 "sync" 26 27 "golang.org/x/net/context" 28 "golang.org/x/oauth2" 29 "golang.org/x/oauth2/google" 30 "golang.org/x/oauth2/jwt" 31 "google.golang.org/grpc/credentials" 32) 33 34// TokenSource supplies PerRPCCredentials from an oauth2.TokenSource. 35type TokenSource struct { 36 oauth2.TokenSource 37} 38 39// GetRequestMetadata gets the request metadata as a map from a TokenSource. 40func (ts TokenSource) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 41 token, err := ts.Token() 42 if err != nil { 43 return nil, err 44 } 45 return map[string]string{ 46 "authorization": token.Type() + " " + token.AccessToken, 47 }, nil 48} 49 50// RequireTransportSecurity indicates whether the credentials requires transport security. 51func (ts TokenSource) RequireTransportSecurity() bool { 52 return true 53} 54 55type jwtAccess struct { 56 jsonKey []byte 57} 58 59// NewJWTAccessFromFile creates PerRPCCredentials from the given keyFile. 60func NewJWTAccessFromFile(keyFile string) (credentials.PerRPCCredentials, error) { 61 jsonKey, err := ioutil.ReadFile(keyFile) 62 if err != nil { 63 return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err) 64 } 65 return NewJWTAccessFromKey(jsonKey) 66} 67 68// NewJWTAccessFromKey creates PerRPCCredentials from the given jsonKey. 69func NewJWTAccessFromKey(jsonKey []byte) (credentials.PerRPCCredentials, error) { 70 return jwtAccess{jsonKey}, nil 71} 72 73func (j jwtAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 74 ts, err := google.JWTAccessTokenSourceFromJSON(j.jsonKey, uri[0]) 75 if err != nil { 76 return nil, err 77 } 78 token, err := ts.Token() 79 if err != nil { 80 return nil, err 81 } 82 return map[string]string{ 83 "authorization": token.TokenType + " " + token.AccessToken, 84 }, nil 85} 86 87func (j jwtAccess) RequireTransportSecurity() bool { 88 return true 89} 90 91// oauthAccess supplies PerRPCCredentials from a given token. 92type oauthAccess struct { 93 token oauth2.Token 94} 95 96// NewOauthAccess constructs the PerRPCCredentials using a given token. 97func NewOauthAccess(token *oauth2.Token) credentials.PerRPCCredentials { 98 return oauthAccess{token: *token} 99} 100 101func (oa oauthAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 102 return map[string]string{ 103 "authorization": oa.token.TokenType + " " + oa.token.AccessToken, 104 }, nil 105} 106 107func (oa oauthAccess) RequireTransportSecurity() bool { 108 return true 109} 110 111// NewComputeEngine constructs the PerRPCCredentials that fetches access tokens from 112// Google Compute Engine (GCE)'s metadata server. It is only valid to use this 113// if your program is running on a GCE instance. 114// TODO(dsymonds): Deprecate and remove this. 115func NewComputeEngine() credentials.PerRPCCredentials { 116 return TokenSource{google.ComputeTokenSource("")} 117} 118 119// serviceAccount represents PerRPCCredentials via JWT signing key. 120type serviceAccount struct { 121 mu sync.Mutex 122 config *jwt.Config 123 t *oauth2.Token 124} 125 126func (s *serviceAccount) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 127 s.mu.Lock() 128 defer s.mu.Unlock() 129 if !s.t.Valid() { 130 var err error 131 s.t, err = s.config.TokenSource(ctx).Token() 132 if err != nil { 133 return nil, err 134 } 135 } 136 return map[string]string{ 137 "authorization": s.t.TokenType + " " + s.t.AccessToken, 138 }, nil 139} 140 141func (s *serviceAccount) RequireTransportSecurity() bool { 142 return true 143} 144 145// NewServiceAccountFromKey constructs the PerRPCCredentials using the JSON key slice 146// from a Google Developers service account. 147func NewServiceAccountFromKey(jsonKey []byte, scope ...string) (credentials.PerRPCCredentials, error) { 148 config, err := google.JWTConfigFromJSON(jsonKey, scope...) 149 if err != nil { 150 return nil, err 151 } 152 return &serviceAccount{config: config}, nil 153} 154 155// NewServiceAccountFromFile constructs the PerRPCCredentials using the JSON key file 156// of a Google Developers service account. 157func NewServiceAccountFromFile(keyFile string, scope ...string) (credentials.PerRPCCredentials, error) { 158 jsonKey, err := ioutil.ReadFile(keyFile) 159 if err != nil { 160 return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err) 161 } 162 return NewServiceAccountFromKey(jsonKey, scope...) 163} 164 165// NewApplicationDefault returns "Application Default Credentials". For more 166// detail, see https://developers.google.com/accounts/docs/application-default-credentials. 167func NewApplicationDefault(ctx context.Context, scope ...string) (credentials.PerRPCCredentials, error) { 168 t, err := google.DefaultTokenSource(ctx, scope...) 169 if err != nil { 170 return nil, err 171 } 172 return TokenSource{t}, nil 173} 174