Cosmos-Server/src/authorizationserver/oauth2.go
2023-10-14 22:37:18 +01:00

144 lines
4.4 KiB
Go

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",
},
},
}
}