3
0
corteza/app/boot_auth.go

156 lines
4.6 KiB
Go

package app
import (
"context"
"fmt"
authHandlers "github.com/cortezaproject/corteza-server/auth/handlers"
"github.com/cortezaproject/corteza-server/auth/oauth2"
"github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/pkg/id"
"github.com/cortezaproject/corteza-server/pkg/options"
"github.com/cortezaproject/corteza-server/store"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/lestrrat-go/jwx/jwa"
"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
"go.uber.org/zap"
"io/ioutil"
"strings"
)
func (app *CortezaApp) initAuth(ctx context.Context) (err error) {
log := app.Log.Named("auth")
if app.Opt.Auth.DefaultClient != "" {
// default client will help streamline authorization with default clients
app.DefaultAuthClient, err = store.LookupAuthClientByHandle(ctx, app.Store, app.Opt.Auth.DefaultClient)
if err != nil {
return fmt.Errorf("cannot load default client: %w", err)
}
}
app.oa2m = oauth2.NewManager(
app.Opt.Auth,
app.Log,
oauth2.NewClientStore(app.Store, app.DefaultAuthClient),
oauth2.NewTokenStore(app.Store),
)
// set base path for links&routes in auth server
authHandlers.BasePath = app.Opt.HTTPServer.BaseUrl
auth.DefaultSigner = auth.HmacSigner(app.Opt.Auth.Secret)
if auth.HttpTokenVerifier, err = auth.TokenVerifierMiddlewareWithSecretSigner(app.Opt.Auth.Secret); err != nil {
return fmt.Errorf("could not set token verifier")
}
// create token signature function from AUTH_ options
// and pass it on to the token issuer
alg, key, err := prepareSignatureFnParams(app.Opt.Auth)
if err != nil {
return fmt.Errorf("could not initialize token signer: %w", err)
}
log.Info(
"initializing JWT and authentication procedures",
zap.Stringer("algoritm", alg),
)
// construct token issuer with algorithm, secrets,
auth.TokenIssuer, err = auth.NewTokenIssuer(
auth.WithSigner(func(t jwt.Token) ([]byte, error) {
return jwt.Sign(t, alg, key)
}),
// @todo implement configurable issuer claim
//auth.WithDefaultIssuer(app.Opt.Auth.TokenClaimIssuer),
auth.WithDefaultExpiration(app.Opt.Auth.AccessTokenLifetime),
auth.WithDefaultClientID(app.DefaultAuthClient.ID),
auth.WithLookup(func(ctx context.Context, accessToken string) (err error) {
_, err = store.LookupAuthOa2tokenByAccess(ctx, app.Store, accessToken)
return err
}),
auth.WithStore(func(ctx context.Context, req auth.TokenRequest) error {
var (
eti = auth.GetExtraReqInfoFromContext(ctx)
createdAt = req.IssuedAt
oa2t = &types.AuthOa2token{
ID: id.Next(),
Access: req.AccessToken,
Refresh: req.RefreshToken,
CreatedAt: createdAt,
RemoteAddr: eti.RemoteAddr,
UserAgent: eti.UserAgent,
ClientID: req.ClientID,
UserID: req.UserID,
ExpiresAt: createdAt.Add(req.Expiration),
}
)
return store.CreateAuthOa2token(ctx, app.Store, oa2t)
}),
)
if err != nil {
return fmt.Errorf("could not initialize token issuer: %w", err)
}
return
}
// helper function that loads and/or parses private keys and initializes JWT signer function
// from the given arguments
func prepareSignatureFnParams(opt options.AuthOpt) (alg jwa.SignatureAlgorithm, key any, err error) {
alg = jwa.SignatureAlgorithm(opt.JwtAlgorithm)
switch alg {
case jwa.HS256, jwa.HS384, jwa.HS512:
// expecting secret to be set
if len(opt.Secret) == 0 {
return alg, nil, fmt.Errorf("token secret missing")
}
key = []byte(opt.Secret)
case
jwa.PS256, jwa.PS384, jwa.PS512,
jwa.RS256, jwa.RS384, jwa.RS512:
if len(opt.JwtKey) == 0 {
return alg, nil, fmt.Errorf("token key missing")
}
// if given key dos not begins with "-----BEGIN"
// assume it's path to a file and load contents of that file
if !strings.HasPrefix(opt.JwtKey, "-----BEGIN") {
var (
keyFile = opt.JwtKey
b []byte
)
if b, err = ioutil.ReadFile(keyFile); err != nil {
return alg, nil, fmt.Errorf("could not read key file: %w", err)
}
// overwrite th input and load
opt.JwtKey = string(b)
// recheck contents of the key
if !strings.HasPrefix(opt.JwtKey, "-----BEGIN") {
return alg, nil, fmt.Errorf("file %q does not contain a valid private key", keyFile)
}
}
// generates pem.Private from the kInput string
key, err = jwk.ParseKey([]byte(opt.JwtKey), jwk.WithPEM(true))
case "":
// should be caught by options init procedure and set to default,
// but you never know...
err = fmt.Errorf("token signature algorithm empty or missing")
default:
err = fmt.Errorf("token signature algorithm %q not supported", alg)
}
return
}