package authorizationserver import ( "math/rand" "crypto/rsa" "github.com/ory/fosite" "time" "net/http" "os" "strings" "crypto/sha256" "encoding/binary" "github.com/ory/fosite/compose" "github.com/ory/fosite/handler/openid" "github.com/ory/fosite/storage" "github.com/ory/fosite/token/jwt" "github.com/azukaar/cosmos-server/src/utils" "github.com/gorilla/mux" ) var oauth2 fosite.OAuth2Provider var AuthPrivateKey *rsa.PrivateKey func Init() { config := utils.ReadConfigFromFile() authKey := config.HTTPConfig.AuthPrivateKey secret := []byte("some-super-cool-secret-that-nobody-knows") if len(authKey) > 64 { secret = []byte(authKey[32:64]) } foconfig := &fosite.Config{ AccessTokenLifespan: time.Minute * 30, GlobalSecret: secret, } store := storage.NewMemoryStore() // loop config.OpenIDClients for _, client := range config.OpenIDClients { utils.Log("Registering OpenID client: " + client.ID) // register client store.Clients[client.ID] = &fosite.DefaultClient{ ID: client.ID, Secret: []byte(client.Secret), RedirectURIs: strings.Split(client.Redirect, ","), Scopes: []string{"openid", "email", "profile", "offline", "roles", "groups", "address", "phone", "role"}, ResponseTypes: []string{"id_token", "code", "token", "id_token token", "code id_token", "code token", "code id_token token"}, GrantTypes: []string{"implicit", "refresh_token", "authorization_code", "password", "client_credentials"}, } } // Compute a hash of your authKey to use as a seed for deterministic RSA key generation seedHash := sha256.Sum256([]byte(authKey)) seed := int64(binary.BigEndian.Uint64(seedHash[:])) // Now you can create a deterministic source of randomness deterministicReader := rand.New(rand.NewSource(seed)) // privateKey is used to sign JWT tokens. The default strategy uses RS256 (RSA Signature with SHA-256) AuthPrivateKey, _ = rsa.GenerateKey(deterministicReader, 2048) // Build a fosite instance with all OAuth2 and OpenID Connect handlers enabled, plugging in our configurations as specified above. oauth2 = compose.ComposeAllEnabled(foconfig, store, AuthPrivateKey) utils.Log("OpenID server initialized") } func RegisterHandlers(wellKnown *mux.Router, userRouter *mux.Router, serverRouter *mux.Router) { // Set up oauth2 endpoints. You could also use gorilla/mux or any other router. userRouter.HandleFunc("/auth", authEndpoint) serverRouter.HandleFunc("/token", tokenEndpoint) // user infos serverRouter.HandleFunc("/userinfo", userInfosEndpoint) // revoke tokens serverRouter.HandleFunc("/revoke", revokeEndpoint) serverRouter.HandleFunc("/introspect", introspectionEndpoint) // public endpoints // set well-known endpoints to be json encoded wellKnown.Use(utils.AcceptHeader("application/json")) wellKnown.HandleFunc("/.well-known/openid-configuration", discoverEndpoint) wellKnown.HandleFunc("/.well-known/jwks.json", jwksEndpoint) } // A session is passed from the `/auth` to the `/token` endpoint. You probably want to store data like: "Who made the request", // "What organization does that person belong to" and so on. // For our use case, the session will meet the requirements imposed by JWT access tokens, HMAC access tokens and OpenID Connect // ID Tokens plus a custom field // newSession is a helper function for creating a new session. This may look like a lot of code but since we are // setting up multiple strategies it is a bit longer. // Usually, you could do: // // session = new(fosite.DefaultSession) func newSession(user string, req *http.Request) *openid.DefaultSession { if user != "" { utils.Debug("Creating new session for user: " + user) } // get hostname from request hostname := req.Host // external request if hostname == utils.GetMainConfig().HTTPConfig.Hostname { if utils.IsHTTPS { hostname = "https://" + hostname } else { hostname = "http://" + hostname } } else if hostname == os.Getenv("HOSTNAME") { hostname = "http://" + hostname } else { utils.Error("Invalid hostname for OpenID request: " + hostname, nil) return nil } return &openid.DefaultSession{ Claims: &jwt.IDTokenClaims{ Issuer: hostname, Subject: user, ExpiresAt: time.Now().Add(time.Hour * 6), IssuedAt: time.Now(), RequestedAt: time.Now(), AuthTime: time.Now(), }, Headers: &jwt.Headers{ Extra: map[string]interface{}{ "kid": "1", }, }, } }