First and foremost, Hydra is capable of being run on GCP without modification. This package is an attempt to bootstrap Hydra utilizing GCP's existing authentication and storage infrastructure (IAM API's signJwt/Cloud Datastore) and not meant to be used unless you know what you are doing. Some caveats with this:
http.Handler
(frontend, backend) are provided to you so you can load your own (e.g. useful when using AppEngine Flexible, adding your own middleware).Well despite the JWT vs opaque token (arguable) con, you get some pros:
Try and make as FEW changes and copy as LITTLE as possible of the original Hydra bootstrap process. All we really want to do is plug in a different signing mechanism for access tokens and store configurations/sessions in datastore, everything else should work exactly as-is from within Hydra.
Onto the good part. There are two things you are responsible for providing to this package in order to boostrap it:
context.Context
with a configuration to be used with jwt-go (see the gcp-jwt-go pacakage)indexes:
- kind: HydraOauth2Access
ancestor: yes
properties:
- name: rat
- kind: HydraJWK
ancestor: yes
properties:
- name: created_at
direction: desc
That's about it. You can continue to use your own web framework so long as you're aware of the handlers already implemented by hydra (basically everything here. What's not supported:
example:
package main
import (
"github.com/justinas/alice"
"github.com/ory/herodot"
"github.com/ory/hydra/config"
"github.com/someone1/gcp-jwt-go"
"github.com/someone1/hydra-gcp"
"google.golang.org/appengine"
//...
)
func main() {
// You can set these up in an app.yaml if using AppEngine Flexible (STANDARD DOESN'T WORK!)
c := &config.Config{
DatabaseURL: os.Getenv("DATABASE_URL"),
SystemSecret: os.Getenv("SYSTEM_SECRET"),
CookieSecret: os.Getenv("SYSTEM_SECRET"),
Issuer: os.Getenv("ISSUER"),
ConsentURL: os.Getenv("CONSENT_URL"),
LoginURL: os.Getenv("LOGIN_URL"),
BCryptWorkFactor: bcrypt.DefaultCost,
OAuth2AccessTokenStrategy: "jwt",
SubjectTypesSupported: "public",
LogLevel: "info",
AccessTokenLifespan: "5m",
AuthCodeLifespan: "10m",
IDTokenLifespan: "1h",
AllowTLSTermination: "0.0.0.0/0",
}
if appengine.IsDevAppServer() {
c.ForceHTTP = true
}
gcpconfig := &gcpjwt.IAMConfig{ServiceAccount: os.Getenv("API_SERVICE_ACCOUNT")}
ctx := context.Background()
logger := c.GetLogger()
w := herodot.NewJSONWriter(logger)
frontend, backend := hydragcp.GenerateIAMHydraHandler(ctx, c, gcpconfig, w, true)
// Protect the backend with something like "github.com/someone1/gcp-jwt-go/jwtmiddleware"
// If we want to host both frontend and backend on the same port - PROTECT THE BACKEND!
combinedMux := http.NewServeMux()
combinedMux.Handle("/oauth2/", frontend)
combinedMux.Handle("/oauth2/auth/sessions/login/revoke", frontend)
combinedMux.Handle("/.well-known/", wellKnownCORSHandler(frontend)) // CORS issue w/ hydra, see #1028
combinedMux.Handle("/userinfo", frontend)
combinedMux.Handle(consent.LoginPath+"/", backend)
combinedMux.Handle(consent.ConsentPath+"/", backend)
combinedMux.Handle("/oauth2/auth/sessions/", backend)
combinedMux.Handle(oauth2.IntrospectPath, backend)
combinedMux.Handle(oauth2.FlushPath, backend)
combinedMux.Handle("/", backend)
http.Handle("/", combinedMux)
appengine.Main() // Or any graceful web server listening on port 8080
}