Skip to content

Commit 911b97d

Browse files
committed
doc: Added more documentation an updated README, moving away from gopkg.in and will just update master branch
1 parent 12c6818 commit 911b97d

File tree

7 files changed

+193
-134
lines changed

7 files changed

+193
-134
lines changed

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
language: go
22
sudo: false
3-
go_import_path: gopkg.in/someone1/gcp-jwt-go.v2
43
go:
54
- 1.10.x
65
- 1.11.x
@@ -9,8 +8,8 @@ addons:
98
packages:
109
- python-rsa
1110
env:
12-
- GOOGLE_APPLICATION_CREDENTIALS="$GOPATH/src/gopkg.in/someone1/gcp-jwt-go.v2/credentials.json"
13-
KMS_TEST_KEYS="$GOPATH/src/gopkg.in/someone1/gcp-jwt-go.v2/kms-test-keys.json"
11+
- GOOGLE_APPLICATION_CREDENTIALS="$GOPATH/src/github.com/someone1/gcp-jwt-go/credentials.json"
12+
KMS_TEST_KEYS="$GOPATH/src/github.com/someone1/gcp-jwt-go/kms-test-keys.json"
1413
before_install:
1514
- openssl aes-256-cbc -K $encrypted_b9c0b4811a94_key -iv $encrypted_b9c0b4811a94_iv -in secrets.tar.enc -out secrets.tar -d
1615
- tar xvf secrets.tar

README.md

Lines changed: 8 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,30 @@
1-
# gcp-jwt-go (v2) [![GoDoc](https://godoc.org/gopkg.in/someone1/gcp-jwt-go.v2?status.svg)](https://godoc.org/gopkg.in/someone1/gcp-jwt-go.v2) [![Go Report Card](https://goreportcard.com/badge/gopkg.in/someone1/gcp-jwt-go.v2)](https://goreportcard.com/report/gopkg.in/someone1/gcp-jwt-go.v2) [![Build Status](https://travis-ci.org/someone1/gcp-jwt-go.svg?branch=v2)](https://travis-ci.org/someone1/gcp-jwt-go) [![Coverage Status](https://coveralls.io/repos/github/someone1/gcp-jwt-go/badge.svg?branch=v2)](https://coveralls.io/github/someone1/gcp-jwt-go?branch=v2)
1+
# gcp-jwt-go [![GoDoc](https://godoc.org/github.com/someone1/gcp-jwt-go?status.svg)](https://godoc.org/github.com/someone1/gcp-jwt-go) [![Go Report Card](https://goreportcard.com/badge/github.com/someone1/gcp-jwt-go)](https://goreportcard.com/report/github.com/someone1/gcp-jwt-go) [![Build Status](https://travis-ci.org/someone1/gcp-jwt-go.svg)](https://travis-ci.org/someone1/gcp-jwt-go) [![Coverage Status](https://coveralls.io/repos/github/someone1/gcp-jwt-go/badge.svg)](https://coveralls.io/github/someone1/gcp-jwt-go)
22

33
Google Cloud Platform (KMS, IAM & AppEngine) jwt-go implementations
44

5-
## New with V2:
5+
## New with v2:
66

77
Google Cloud KMS [now supports signatures](https://cloud.google.com/kms/docs/create-validate-signatures) and support has been added to gcp-jwt-go!
88

9-
## Breaking Changes with V2
9+
## Breaking Changes with v2
1010

1111
- Package name changed from gcp_jwt to gcpjwt
1212
- Refactoring of code (including exported functions/structs)
1313
- Certificate caching is now opt-in vs opt-out
1414

1515
To continue using the older version, please import as follows: `import "gopkg.in/someone1/gcp-jwt-go.v1"`
1616

17-
### Other Features
17+
### Features
1818

19-
gcp-jwt-go has a basic implementation of using the [IAM SignJwt API](https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signJwt) on Google Cloud Platform to sign JWT tokens using the dgrijalva/jwt-go package. Should work across most environments (including AppEngine)!
19+
gcp-jwt-go has basic implementations of using [Google Cloud KMS](https://cloud.google.com/kms/docs/create-validate-signatures), Google IAM API (both [signJwt](https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signJwt) and [signBlob](https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signBlob)), and the [App Identity API](https://cloud.google.com/appengine/docs/go/appidentity/) from AppEngine Standard on Google Cloud Platform to sign JWT tokens using the [dgrijalva/jwt-go](https://github.com/dgrijalva/jwt-go) package. Should work across virtually all environments, on or off of Google's Cloud Platform.
2020

21-
The old method of using the [IAM SignBlob API](https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signBlob) is still supported.
21+
## Getting Started
2222

23-
## AppEngine Only (legacy)
24-
25-
Basic implementation of using the built-in [App Identity API](https://cloud.google.com/appengine/docs/go/appidentity/) of AppEngine to sign JWT tokens using the dgrijalva/jwt-go package.
26-
27-
## Basic usage (using the IAM API):
28-
29-
### Setup
30-
31-
```go
32-
import (
33-
"gopkg.in/someone1/gcp-jwt-go.v2"
34-
)
35-
36-
func init() {
37-
// Unless we want to keep the original RS256 implementation alive, override it (recommended)
38-
gcpjwt.SigningMethodIAMJWT.Override() // For signJwt
39-
// OR
40-
gcpjwt.SigningMethodIAMBlob.Override() // For signBlob
41-
}
42-
```
43-
44-
### Create a Token
45-
46-
```go
47-
import (
48-
"context"
49-
50-
"github.com/dgrijalva/jwt-go"
51-
"gopkg.in/someone1/gcp-jwt-go.v2"
52-
)
53-
54-
func makeToken() string {
55-
token := jwt.New(gcpjwt.SigningMethodGCPJWT)
56-
config := &gcpjwt.IAMConfig{
57-
ServiceAccount: "[email protected]",
58-
IAMType: gcpjwt.IAMJwtType, // or gcpjwt.IAMBlobType
59-
}
60-
ctx := gcpjwt.NewIAMContext(context.Background(), config)
61-
token.Method = gcpjwt.SigningMethodIAMJWT // or gcpjwt.SigningMethodIAMBlob
62-
63-
// Fill in Token claims
64-
65-
// For signBlob
66-
tokenString, err := token.SignedString(ctx)
67-
68-
// For signJwt
69-
// !!IMPORTANT!! Due to the way the signJwt API returns tokens, we can't use the standard signing process
70-
// to sign
71-
signingString, err := token.SigningString()
72-
// handle err
73-
tokenString, terr := token.Method.Sign(signingString, ctx)
74-
// handle terr
75-
76-
return tokenString
77-
}
78-
```
79-
80-
### Validate a Token
81-
82-
```go
83-
import (
84-
"context"
85-
"strings"
86-
87-
"github.com/dgrijalva/jwt-go"
88-
"gopkg.in/someone1/gcp-jwt-go.v2"
89-
)
90-
91-
func validateToken(tokenString string) {
92-
config := &gcpjwt.IAMConfig{
93-
ServiceAccount: "[email protected]",
94-
IAMType: gcpjwt.IAMJwtType, // or gcpjwt.IAMBlobType
95-
}
96-
config.EnableCache = true // Enable certificates cache
97-
98-
// To Verify (if we called Override() for our method type prior)
99-
token, err := jwt.Parse(tokenString, gcpjwt.VerfiyKeyfunc(context.Background(), config))
100-
101-
// If we DID NOT call the Override() function
102-
// This is basically copying the https://github.com/dgrijalva/jwt-go/blob/master/parser.go#L23 ParseWithClaims function here but forcing our own method vs getting one based on the Alg field
103-
// Or Try and parse, Ignore the result and try with the proper method:
104-
token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
105-
return nil, nil
106-
})
107-
parts := strings.Split(token.Raw, ".")
108-
token.Method = gcpjwt.SigningMethodIAMJWT // or gcpjwt.SigningMethodIAMBlob
109-
if err := token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, ctx); err != nil {
110-
// handle error
111-
} else {
112-
token.Valid = true
113-
}
114-
}
115-
```
116-
117-
## Basic usage (old - use with AppEngine Standard):
118-
119-
```go
120-
import (
121-
"github.com/dgrijalva/jwt-go"
122-
"gopkg.in/someone1/gcp-jwt-go.v2"
123-
)
124-
125-
// AppEngine Only Method
126-
token := jwt.New(gcpjwt.SigningMethodAppEngine)
127-
128-
// OR
129-
130-
token := jwt.New(gcpjwt.SigningMethodGCP)
131-
config := &gcpjwt.IAMSignBlobConfig{
132-
ServiceAccount: "[email protected]",
133-
}
134-
ctx := gcpjwt.NewContext(ctx, config)
135-
136-
// Pass in a context.Context as the key for Sign/Verify
137-
// Same process as any other signing method in the jwt-go package
138-
```
23+
Please read the documentation at [https://godoc.org/github.com/someone1/gcp-jwt-go](https://godoc.org/github.com/someone1/gcp-jwt-go)
13924

14025
## Tips
14126

142-
- Create a separate service account to sign on behalf of for your projects unless you NEED to use your default service account (e.g. the AppEngine service account). This way you can limit the scope of access for any leaked credentials. You'll have to grant the `roles/iam.serviceAccountTokenCreator` role to any user/group/serviceaccount you want to be able to sign on behalf of the new service account (resource: `projects/-/serviceAccounts/<serviceaccount>`).
27+
- If using the IAM API - create a separate service account to sign on behalf of for your projects unless you NEED to use your default service account (e.g. the AppEngine service account). This way you can limit the scope of access for any leaked credentials. You'll have to grant the `roles/iam.serviceAccountTokenCreator` role to any user/group/serviceaccount you want to be able to sign on behalf of the new service account (resource: `projects/-/serviceAccounts/<serviceaccount>`). For example, create an api-signer service account, do NOT furnish any keys for it, [grant](https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/setIamPolicy) your AppEngine/GCE/etc. default service account the proper role for that serviceAccount, and use the api-signer@... service account address in your configuration.
14328
- If using outside of GCP, be sure to put credentials for an account that can access the service account for signing tokens in a well known location:
14429
1. A JSON file whose path is specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable.
14530
2. A JSON file in a location known to the gcloud command-line tool. On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. On other systems, $HOME/.config/gcloud/application_default_credentials.json.

doc.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
Package gcp-jwt-go has basic implementations of using Google Cloud KMS, Google IAM API (both signJwt and signBlob),
3+
and the App Identity API from AppEngine Standard to sign JWT tokens using the package
4+
"github.com/dgrijalva/jwt-go". Should work across virtually all environments, on or off of Google's Cloud Platform.
5+
6+
Getting Started
7+
8+
It is highly reccomended that you override the default algorithm implementations that you want to leverage a GCP service
9+
for in dgrijalva/jwt-go. You otherwise will have to manually pick the verification method for your JWTs and they will
10+
place non-standard headers in the rendered JWT (with the exception of signJwt from the IAM API which overwrites the
11+
header with its own).
12+
13+
You should only need to override the algorithm(s) you plan to use. It is also incorrect to override overlapping,
14+
algorithms such as `gcpjwt.SigningMethodKMSRS256.Override()` and `gcpjwt.SigningMethodIAMJWT.Override()`
15+
16+
Example:
17+
18+
import (
19+
"github.com/someone1/gcp-jwt-go"
20+
)
21+
22+
func init() {
23+
// Pick one or more of the following to override
24+
25+
// Cloud KMS
26+
gcpjwt.SigningMethodKMSRS256.Override() // RS256
27+
gcpjwt.SigningMethodKMSPS256.Override() // PS256
28+
gcpjwt.SigningMethodKMSES256.Override() // ES256
29+
gcpjwt.SigningMethodKMSES384.Override() // ES384
30+
31+
// IAM API - This implements RS256 exclusively
32+
gcpjwt.SigningMethodIAMJWT.Override() // For signJwt
33+
gcpjwt.SigningMethodIAMBlob.Override() // For signBlob
34+
35+
// AppEngine - Standard runtime only, does not apply to Flexible runtime, implements RS256 exclusively
36+
// You can also use any of the above methods on AppEngine Standard
37+
gcpjwt.SigningMethodAppEngine.Override()
38+
}
39+
40+
As long as a you override a default algorithm implementation as shown above, using the dgrijalva/jwt-go is mostly unchanged.
41+
42+
Create a Token
43+
44+
Token creation is more/less done the same way as in the dgrijalva/jwt-go package. The key that you need to provide is
45+
always going to be a context.Context, usuaully with a configuration object loaded in:
46+
- use gcpjwt.IAMConfig for the SigningMethodIAMJWT and SigningMethodIAMBlob signing methods
47+
- use an appengine.NewContext for the SigningMethodAppEngine signing method
48+
- use gcpjwt.KMSConfig for any of the KMS signing methods
49+
50+
Example:
51+
52+
53+
import (
54+
"context"
55+
"net/http"
56+
57+
"github.com/dgrijalva/jwt-go"
58+
"github.com/someone1/gcp-jwt-go"
59+
"google.golang.org/appengine" // only on AppEngine Standard when using the SigningMethodAppEngine signing method
60+
)
61+
62+
func makeToken(ctx context.Context) (string, error) string {
63+
// Important - if on AppEngine standard, even if you aren't useing the SigningMethodAppEngine signing method
64+
// you must pass around the appengine.NewContext context as it is required for the API calls all methods must
65+
// make.
66+
67+
var key interface{}
68+
claims := &jwt.StandardClaims{
69+
ExpiresAt: 15000,
70+
Issuer: "test",
71+
}
72+
token := jwt.NewWithClaims(gcpjwt.SigningMethodGCPJWT, claims)
73+
74+
// Prepare your signing key
75+
76+
// For SigningMethodIAMJWT or SigningMethodIAMBlob
77+
config := &gcpjwt.IAMConfig{
78+
ServiceAccount: "[email protected]",
79+
IAMType: gcpjwt.IAMJwtType, // or gcpjwt.IAMBlobType
80+
}
81+
key = gcpjwt.NewIAMContext(ctx, config)
82+
83+
// For any KMS signing method
84+
config := &gcpjwt.KMSConfig{
85+
KeyPath: "name=projects/<project-id>/locations/<location>/keyRings/<key-ring-name>/cryptoKeys/<key-name>/cryptoKeyVersions/<key-version>",
86+
}
87+
key = gcpjwt.NewKMSContext(ctx, config)
88+
89+
// For SigningMethodAppEngine
90+
key = ctx
91+
92+
// For all signing methods EXCEPT signJWT
93+
return token.SignedString(key)
94+
95+
// For signJwt
96+
// !!IMPORTANT!! Due to the way the signJwt API returns tokens, we can't use the standard signing process
97+
// to sign
98+
signingString, err := token.SigningString()
99+
if err != nil {
100+
return "", err
101+
}
102+
103+
return token.Method.Sign(signingString, key)
104+
}
105+
106+
Validate a Token
107+
108+
Finally, the steps to validate a token should be straight forward. This library provides you with helper jwt.Keyfunc
109+
implementations to do the heavy lifting around getting the public certificates for verification:
110+
111+
- gcpjwt.IAMVerfiyKeyfunc can be used for the IAM API and the AppEngine Standard signing methods
112+
- gcpjwt.AppEngineVerfiyKeyfunc is only available on AppEngine standard and can only be used on JWT signed from the same default service account as the running application
113+
- gcp.KMSVerfiyKeyfunc can be used for the Cloud KMS signing methods
114+
115+
Example:
116+
117+
import (
118+
"context"
119+
"time"
120+
"strings"
121+
122+
"github.com/dgrijalva/jwt-go"
123+
"github.com/someone1/gcp-jwt-go"
124+
)
125+
126+
func validateToken(ctx context.Context, tokenString string) (*jwt.Token, error) {
127+
// Important - if on AppEngine standard, even if you aren't useing the SigningMethodAppEngine signing method
128+
// you must pass around the appengine.NewContext context as it is required for the API calls all methods must
129+
// make.
130+
131+
var keyFunc jwt.Keyfunc
132+
133+
// Prepare your key function
134+
135+
// For SigningMethodIAMJWT or SigningMethodIAMBlob or SigningMethodAppEngine
136+
config := &gcpjwt.IAMConfig{
137+
ServiceAccount: "[email protected]",
138+
IAMType: gcpjwt.IAMJwtType, // or gcpjwt.IAMBlobType (use the Blob type if you used the SigningMethodAppEngine before)
139+
EnableCache: true, // Enable the certificate cache so we don't fetch it on every verification - RECOMMENDED
140+
}
141+
keyFunc = gcpjwt.IAMVerfiyKeyfunc(ctx, config)
142+
143+
// For any KMS signing method
144+
config := &gcpjwt.KMSConfig{
145+
KeyPath: "name=projects/<project-id>/locations/<location>/keyRings/<key-ring-name>/cryptoKeys/<key-name>/cryptoKeyVersions/<key-version>",
146+
}
147+
keyFunc = gcpjwt.KMSVerfiyKeyfunc(ctx, config)
148+
149+
// For SigningMethodAppEngine only on AppEngine Standard
150+
keyFunc = gcpjwt.AppEngineVerfiyKeyfunc(ctx, true, time.Hour)
151+
152+
// If you called an Override function as recommended above, for both signing and verifying a token, you can use
153+
// the regular verification steps - and the same goes if you did NOT call it for both signing and verifying (using non-standard 'alg' headers)
154+
// EXCEPT for the signJwt IAM API signing method which overwrites the header's alg to RS256
155+
return jwt.Parse(tokenString, keyFunc)
156+
157+
// The following is an extreme and advanced use-case - it is NOT recommended but here for those who need it.
158+
//
159+
// If we need to manually override the detected jwt.SigningMethod based on the 'alg' header
160+
// This is basically copying the https://github.com/dgrijalva/jwt-go/blob/master/parser.go#L23 ParseWithClaims function here but forcing our own method vs getting one based on the Alg field
161+
// Or Try and parse, Ignore the result and try with the proper method:
162+
token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
163+
return nil, nil
164+
})
165+
parts := strings.Split(token.Raw, ".")
166+
token.Method = gcpjwt.SigningMethodIAMJWT // or whichever method you want to force
167+
if err := token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, keyFunc); err != nil {
168+
return nil, err
169+
} else {
170+
token.Valid = true
171+
}
172+
return token, nil
173+
}
174+
*/
175+
package gcpjwt

jwtmiddleware/helpers_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import (
1717
"google.golang.org/appengine"
1818
"google.golang.org/appengine/aetest"
1919

20-
"gopkg.in/someone1/gcp-jwt-go.v2"
21-
goauth2 "gopkg.in/someone1/gcp-jwt-go.v2/oauth2"
20+
"github.com/someone1/gcp-jwt-go"
21+
goauth2 "github.com/someone1/gcp-jwt-go/oauth2"
2222
)
2323

2424
var jwtConfig *gjwt.Config

jwtmiddleware/middleware.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/dgrijalva/jwt-go"
1111
"github.com/dgrijalva/jwt-go/request"
1212

13-
"gopkg.in/someone1/gcp-jwt-go.v2"
13+
"github.com/someone1/gcp-jwt-go"
1414
)
1515

1616
// NewHandler will return a middleware that will try and validate tokens in incoming HTTP requests.
@@ -19,7 +19,7 @@ import (
1919
// Audience claim to the one provided, or use https:// + request.Host if blank. NOTE: If using the signJwt method,
2020
// you MUST call gcpjwt.SigningMethodIAMJWT.Override().
2121
//
22-
// Complimentary to https://gopkg.in/someone1/gcp-jwt-go.v2/oauth2
22+
// Complimentary to https://github.com/someone1/gcp-jwt-go/oauth2
2323
func NewHandler(ctx context.Context, config *gcpjwt.IAMConfig, audience string) func(http.Handler) http.Handler {
2424
ctx = gcpjwt.NewIAMContext(ctx, config)
2525

jwtmiddleware/middleware_appengine.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import (
1111
"github.com/dgrijalva/jwt-go/request"
1212
"google.golang.org/appengine"
1313

14-
"gopkg.in/someone1/gcp-jwt-go.v2"
14+
"github.com/someone1/gcp-jwt-go"
1515
)
1616

1717
// NewHandler will return a middleware that will try and validate tokens in incoming HTTP requests.
1818
// The token is expected as a Bearer token in the Authorization header and expected to have an Issuer
1919
// claim equal to the ServiceAccount the provided IAMConfig is configured for. This will also validate the
2020
// Audience claim to the one provided, or use https:// + request.Host if blank.
2121
//
22-
// Complimentary to https://gopkg.in/someone1/gcp-jwt-go.v2/oauth2
22+
// Complimentary to https://github.com/someone1/gcp-jwt-go/oauth2
2323
func NewHandler(_ context.Context, config *gcpjwt.IAMConfig, audience string) func(http.Handler) http.Handler {
2424
return func(h http.Handler) http.Handler {
2525
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)