Add Auth0 JWT support
This commit is contained in:
parent
a897158bcc
commit
83f90c1bbd
|
@ -2,3 +2,4 @@ example
|
||||||
tmp
|
tmp
|
||||||
*.md
|
*.md
|
||||||
web/build
|
web/build
|
||||||
|
docker-compose.*
|
||||||
|
|
40
README.md
40
README.md
|
@ -234,7 +234,7 @@ auth:
|
||||||
# type: jwt
|
# type: jwt
|
||||||
# cookie: _app_session
|
# cookie: _app_session
|
||||||
# secret: abc335bfcfdb04e50db5bb0a4d67ab9
|
# secret: abc335bfcfdb04e50db5bb0a4d67ab9
|
||||||
# public_key_file: abc335bfcfdb04e50db5bb0a4d67ab9
|
# public_key_file: /secrets/public_key.pem
|
||||||
# public_key_type: ecdsa #rsa
|
# public_key_type: ecdsa #rsa
|
||||||
|
|
||||||
database:
|
database:
|
||||||
|
@ -283,10 +283,44 @@ SG_AUTH_URL
|
||||||
SG_AUTH_PASSWORD
|
SG_AUTH_PASSWORD
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
You can only have one type of authentication enabled. You can either pick Rails or JWT. Uncomment the one you use and leave the rest commented out.
|
||||||
|
|
||||||
|
#### JWT Tokens
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
auth:
|
||||||
|
type: jwt
|
||||||
|
provider: auth0 #none
|
||||||
|
cookie: _app_session
|
||||||
|
secret: abc335bfcfdb04e50db5bb0a4d67ab9
|
||||||
|
public_key_file: /secrets/public_key.pem
|
||||||
|
public_key_type: ecdsa #rsa
|
||||||
|
```
|
||||||
|
|
||||||
|
For JWT tokens we currently support tokens from a provider like Auth0
|
||||||
|
or if you have a custom solution then we look for the `user_id` in the
|
||||||
|
`subject` claim of of the `id token`. If you pick Auth0 then we derive two variables from the token `user_id` and `user_id_provider` for to use in your filters.
|
||||||
|
|
||||||
|
We can get the JWT token either from the `authorization` header where we expect it to be a `bearer` token or if `cookie` is specified then we look there.
|
||||||
|
|
||||||
|
For verified either a `secret` or a public key (ecdsa or rsa) is required. When using public keys they have to be in a PEM format file.
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
How do I deploy the Super Graph service with my existing rails app? You have several options here. Esentially you need to ensure your app's session cookie
|
How do I deploy the Super Graph service with my existing rails app? You have several options here. Esentially you need to ensure your app's session cookie will be passed to this service.
|
||||||
will be passed to this service.
|
|
||||||
|
#### Custom Docker Image
|
||||||
|
|
||||||
|
Create a `Dockerfile` like the one below to roll your own
|
||||||
|
custom Super Graph docker image. And to build it `docker build -t my-super-graph .`
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM dosco/super-graph:latest
|
||||||
|
WORKDIR /app
|
||||||
|
COPY *.yml ./
|
||||||
|
```
|
||||||
|
|
||||||
#### Deploy under a subdomain
|
#### Deploy under a subdomain
|
||||||
For this to work you have to ensure that the option `:domain => :all` is added to your rails app config `Application.config.session_store` this will cause your rails app to create session cookies that can be shared with sub-domains. More info here <http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/>
|
For this to work you have to ensure that the option `:domain => :all` is added to your rails app config `Application.config.session_store` this will cause your rails app to create session cookies that can be shared with sub-domains. More info here <http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/>
|
||||||
|
|
3
dev.yml
3
dev.yml
|
@ -50,9 +50,10 @@ auth:
|
||||||
|
|
||||||
# auth:
|
# auth:
|
||||||
# type: jwt
|
# type: jwt
|
||||||
|
# provider: auth0
|
||||||
# cookie: _app_session
|
# cookie: _app_session
|
||||||
# secret: abc335bfcfdb04e50db5bb0a4d67ab9
|
# secret: abc335bfcfdb04e50db5bb0a4d67ab9
|
||||||
# public_key_file: abc335bfcfdb04e50db5bb0a4d67ab9
|
# public_key_file: /secrets/public_key.pem
|
||||||
# public_key_type: ecdsa #rsa
|
# public_key_type: ecdsa #rsa
|
||||||
|
|
||||||
database:
|
database:
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,7 +1,7 @@
|
||||||
module github.com/dosco/super-graph
|
module github.com/dosco/super-graph
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/adjust/gorails v0.0.0-20171013043634-2786ed0c03d3
|
github.com/adjust/gorails v0.0.0-20171013043634-2786ed0c03d3
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737
|
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/garyburd/redigo v1.6.0
|
github.com/garyburd/redigo v1.6.0
|
||||||
|
|
3
prod.yml
3
prod.yml
|
@ -43,9 +43,10 @@ auth:
|
||||||
|
|
||||||
# auth:
|
# auth:
|
||||||
# type: jwt
|
# type: jwt
|
||||||
|
# provider: auth0
|
||||||
# cookie: _app_session
|
# cookie: _app_session
|
||||||
# secret: abc335bfcfdb04e50db5bb0a4d67ab9
|
# secret: abc335bfcfdb04e50db5bb0a4d67ab9
|
||||||
# public_key_file: abc335bfcfdb04e50db5bb0a4d67ab9
|
# public_key_file: /secrets/public_key.pem
|
||||||
# public_key_type: ecdsa #rsa
|
# public_key_type: ecdsa #rsa
|
||||||
|
|
||||||
database:
|
database:
|
||||||
|
|
|
@ -15,8 +15,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
userIDKey = struct{}{}
|
userIDProviderKey = struct{}{}
|
||||||
errSessionData = errors.New("error decoding session data")
|
userIDKey = struct{}{}
|
||||||
|
errSessionData = errors.New("error decoding session data")
|
||||||
)
|
)
|
||||||
|
|
||||||
func headerHandler(next http.HandlerFunc) http.HandlerFunc {
|
func headerHandler(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
|
|
@ -4,15 +4,27 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
jwtBase int = iota
|
||||||
|
jwtAuth0
|
||||||
|
)
|
||||||
|
|
||||||
func jwtHandler(next http.HandlerFunc) http.HandlerFunc {
|
func jwtHandler(next http.HandlerFunc) http.HandlerFunc {
|
||||||
var key interface{}
|
var key interface{}
|
||||||
|
var jwtProvider int
|
||||||
|
|
||||||
cookie := conf.GetString("auth.cookie")
|
cookie := conf.GetString("auth.cookie")
|
||||||
|
|
||||||
|
provider := conf.GetString("auth.provider")
|
||||||
|
if provider == "auth0" {
|
||||||
|
jwtProvider = jwtAuth0
|
||||||
|
}
|
||||||
|
|
||||||
conf.BindEnv("auth.secret", "SG_AUTH_SECRET")
|
conf.BindEnv("auth.secret", "SG_AUTH_SECRET")
|
||||||
secret := conf.GetString("auth.secret")
|
secret := conf.GetString("auth.secret")
|
||||||
|
|
||||||
|
@ -75,7 +87,17 @@ func jwtHandler(next http.HandlerFunc) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims, ok := token.Claims.(*jwt.StandardClaims); ok {
|
if claims, ok := token.Claims.(*jwt.StandardClaims); ok {
|
||||||
ctx := context.WithValue(r.Context(), userIDKey, claims.Id)
|
ctx := r.Context()
|
||||||
|
|
||||||
|
if jwtProvider == jwtAuth0 {
|
||||||
|
sub := strings.Split(claims.Subject, "|")
|
||||||
|
if len(sub) != 2 {
|
||||||
|
ctx = context.WithValue(ctx, userIDProviderKey, sub[0])
|
||||||
|
ctx = context.WithValue(ctx, userIDKey, sub[1])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx = context.WithValue(ctx, userIDKey, claims.Subject)
|
||||||
|
}
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
serv/http.go
15
serv/http.go
|
@ -153,15 +153,24 @@ func authCheck(ctx context.Context) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func varValues(ctx context.Context) map[string]interface{} {
|
func varValues(ctx context.Context) map[string]interface{} {
|
||||||
userIDFn := fasttemplate.TagFunc(func(w io.Writer, _ string) (int, error) {
|
uidFn := fasttemplate.TagFunc(func(w io.Writer, _ string) (int, error) {
|
||||||
if v := ctx.Value(userIDKey); v != nil {
|
if v := ctx.Value(userIDKey); v != nil {
|
||||||
return w.Write([]byte(v.(string)))
|
return w.Write([]byte(v.(string)))
|
||||||
}
|
}
|
||||||
return 0, errNoUserID
|
return 0, errNoUserID
|
||||||
})
|
})
|
||||||
|
|
||||||
|
uidpFn := fasttemplate.TagFunc(func(w io.Writer, _ string) (int, error) {
|
||||||
|
if v := ctx.Value(userIDProviderKey); v != nil {
|
||||||
|
return w.Write([]byte(v.(string)))
|
||||||
|
}
|
||||||
|
return 0, errNoUserID
|
||||||
|
})
|
||||||
|
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"USER_ID": userIDFn,
|
"USER_ID": uidFn,
|
||||||
"user_id": userIDFn,
|
"user_id": uidFn,
|
||||||
|
"USER_ID_PROVIDER": uidpFn,
|
||||||
|
"user_id_provider": uidpFn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue