package auth import ( "net/http" "strings" "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/module" "forge.cadoles.com/arcad/edge/pkg/module/util" "github.com/dop251/goja" "github.com/golang-jwt/jwt" "github.com/pkg/errors" ) const ( AnonymousSubject = "anonymous" ) type Module struct { server *app.Server keyFunc jwt.Keyfunc } func (m *Module) Name() string { return "auth" } func (m *Module) Export(export *goja.Object) { if err := export.Set("getSubject", m.getSubject); err != nil { panic(errors.Wrap(err, "could not set 'getSubject' function")) } if err := export.Set("ANONYMOUS", AnonymousSubject); err != nil { panic(errors.Wrap(err, "could not set 'ANONYMOUS_USER' property")) } } func (m *Module) getSubject(call goja.FunctionCall, rt *goja.Runtime) goja.Value { ctx := util.AssertContext(call.Argument(0), rt) req, ok := ctx.Value(module.ContextKeyOriginRequest).(*http.Request) if !ok { panic(errors.New("could not find http request in context")) } rawToken := strings.TrimPrefix(req.Header.Get("Authorization"), "Bearer ") if rawToken == "" { rawToken = req.URL.Query().Get("token") } if rawToken == "" { return rt.ToValue(AnonymousSubject) } token, err := jwt.Parse(rawToken, m.keyFunc) if err != nil { panic(errors.WithStack(err)) } if !token.Valid { panic(errors.Errorf("invalid jwt token: '%v'", token.Raw)) } mapClaims, ok := token.Claims.(jwt.MapClaims) if !ok { panic(errors.Errorf("unexpected claims type '%T'", token.Claims)) } subject, exists := mapClaims["sub"] if !exists { return rt.ToValue(AnonymousSubject) } return rt.ToValue(subject) } func ModuleFactory(keyFunc jwt.Keyfunc) app.ServerModuleFactory { return func(server *app.Server) app.ServerModule { return &Module{ server: server, keyFunc: keyFunc, } } }