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