diff --git a/dashboard/src/constants.ts b/dashboard/src/constants.ts
index 1ed810d..e4737e5 100644
--- a/dashboard/src/constants.ts
+++ b/dashboard/src/constants.ts
@@ -2,6 +2,7 @@ export const LOGO_URL =
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
export const TextInputType = {
+ ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME',
CLIENT_ID: 'CLIENT_ID',
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
@@ -125,4 +126,5 @@ export interface envVarTypes {
DATABASE_NAME: string;
DATABASE_TYPE: string;
DATABASE_URL: string;
+ ACCESS_TOKEN_EXPIRY_TIME: string;
}
diff --git a/dashboard/src/graphql/queries/index.ts b/dashboard/src/graphql/queries/index.ts
index a7d0142..1adf02c 100644
--- a/dashboard/src/graphql/queries/index.ts
+++ b/dashboard/src/graphql/queries/index.ts
@@ -53,6 +53,7 @@ export const EnvVariablesQuery = `
DATABASE_NAME,
DATABASE_TYPE,
DATABASE_URL,
+ ACCESS_TOKEN_EXPIRY_TIME,
}
}
`;
diff --git a/dashboard/src/pages/Environment.tsx b/dashboard/src/pages/Environment.tsx
index b4fd091..c51b95a 100644
--- a/dashboard/src/pages/Environment.tsx
+++ b/dashboard/src/pages/Environment.tsx
@@ -85,6 +85,7 @@ export default function Environment() {
DATABASE_NAME: '',
DATABASE_TYPE: '',
DATABASE_URL: '',
+ ACCESS_TOKEN_EXPIRY_TIME: '',
});
const [fieldVisibility, setFieldVisibility] = React.useState<
@@ -600,19 +601,35 @@ export default function Environment() {
- Custom Access Token Scripts
+ Access Token
-
+
+ Access Token Expiry Time:
+
+
+
+
+
+
+ Custom Access Token Scripts:
+
+
+
-
+
diff --git a/server/constants/env.go b/server/constants/env.go
index f391750..d9af456 100644
--- a/server/constants/env.go
+++ b/server/constants/env.go
@@ -21,6 +21,8 @@ const (
// EnvKeyPort key for env variable PORT
EnvKeyPort = "PORT"
+ // EnvKeyAccessTokenExpiryTime key for env variable ACCESS_TOKEN_EXPIRY_TIME
+ EnvKeyAccessTokenExpiryTime = "ACCESS_TOKEN_EXPIRY_TIME"
// EnvKeyAdminSecret key for env variable ADMIN_SECRET
EnvKeyAdminSecret = "ADMIN_SECRET"
// EnvKeyDatabaseType key for env variable DATABASE_TYPE
diff --git a/server/env/env.go b/server/env/env.go
index 8770441..728bb6b 100644
--- a/server/env/env.go
+++ b/server/env/env.go
@@ -120,6 +120,10 @@ func InitAllEnv() error {
}
}
+ if envData.StringEnv[constants.EnvKeyAccessTokenExpiryTime] == "" {
+ envData.StringEnv[constants.EnvKeyAccessTokenExpiryTime] = os.Getenv(constants.EnvKeyAccessTokenExpiryTime)
+ }
+
if envData.StringEnv[constants.EnvKeyAdminSecret] == "" {
envData.StringEnv[constants.EnvKeyAdminSecret] = os.Getenv(constants.EnvKeyAdminSecret)
}
diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go
index 44523e8..e33e660 100644
--- a/server/graph/generated/generated.go
+++ b/server/graph/generated/generated.go
@@ -53,6 +53,7 @@ type ComplexityRoot struct {
}
Env struct {
+ AccessTokenExpiryTime func(childComplexity int) int
AdminSecret func(childComplexity int) int
AllowedOrigins func(childComplexity int) int
AppURL func(childComplexity int) int
@@ -299,6 +300,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.AuthResponse.User(childComplexity), true
+ case "Env.ACCESS_TOKEN_EXPIRY_TIME":
+ if e.complexity.Env.AccessTokenExpiryTime == nil {
+ break
+ }
+
+ return e.complexity.Env.AccessTokenExpiryTime(childComplexity), true
+
case "Env.ADMIN_SECRET":
if e.complexity.Env.AdminSecret == nil {
break
@@ -1381,6 +1389,7 @@ type Response {
}
type Env {
+ ACCESS_TOKEN_EXPIRY_TIME: String
ADMIN_SECRET: String
DATABASE_NAME: String!
DATABASE_URL: String!
@@ -1432,6 +1441,7 @@ type GenerateJWTKeysResponse {
}
input UpdateEnvInput {
+ ACCESS_TOKEN_EXPIRY_TIME: String
ADMIN_SECRET: String
CUSTOM_ACCESS_TOKEN_SCRIPT: String
OLD_ADMIN_SECRET: String
@@ -2221,6 +2231,38 @@ func (ec *executionContext) _AuthResponse_user(ctx context.Context, field graphq
return ec.marshalOUser2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUser(ctx, field.Selections, res)
}
+func (ec *executionContext) _Env_ACCESS_TOKEN_EXPIRY_TIME(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ fc := &graphql.FieldContext{
+ Object: "Env",
+ Field: field,
+ Args: nil,
+ IsMethod: false,
+ IsResolver: false,
+ }
+
+ ctx = graphql.WithFieldContext(ctx, fc)
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.AccessTokenExpiryTime, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ return graphql.Null
+ }
+ res := resTmp.(*string)
+ fc.Result = res
+ return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
+}
+
func (ec *executionContext) _Env_ADMIN_SECRET(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@@ -8093,6 +8135,14 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
for k, v := range asMap {
switch k {
+ case "ACCESS_TOKEN_EXPIRY_TIME":
+ var err error
+
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("ACCESS_TOKEN_EXPIRY_TIME"))
+ it.AccessTokenExpiryTime, err = ec.unmarshalOString2ᚖstring(ctx, v)
+ if err != nil {
+ return it, err
+ }
case "ADMIN_SECRET":
var err error
@@ -8711,6 +8761,8 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("Env")
+ case "ACCESS_TOKEN_EXPIRY_TIME":
+ out.Values[i] = ec._Env_ACCESS_TOKEN_EXPIRY_TIME(ctx, field, obj)
case "ADMIN_SECRET":
out.Values[i] = ec._Env_ADMIN_SECRET(ctx, field, obj)
case "DATABASE_NAME":
diff --git a/server/graph/model/models_gen.go b/server/graph/model/models_gen.go
index ea3b5bd..09468b4 100644
--- a/server/graph/model/models_gen.go
+++ b/server/graph/model/models_gen.go
@@ -24,6 +24,7 @@ type DeleteUserInput struct {
}
type Env struct {
+ AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
AdminSecret *string `json:"ADMIN_SECRET"`
DatabaseName string `json:"DATABASE_NAME"`
DatabaseURL string `json:"DATABASE_URL"`
@@ -179,6 +180,7 @@ type UpdateAccessInput struct {
}
type UpdateEnvInput struct {
+ AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
AdminSecret *string `json:"ADMIN_SECRET"`
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls
index a4f29f9..841ad8c 100644
--- a/server/graph/schema.graphqls
+++ b/server/graph/schema.graphqls
@@ -87,6 +87,7 @@ type Response {
}
type Env {
+ ACCESS_TOKEN_EXPIRY_TIME: String
ADMIN_SECRET: String
DATABASE_NAME: String!
DATABASE_URL: String!
@@ -138,6 +139,7 @@ type GenerateJWTKeysResponse {
}
input UpdateEnvInput {
+ ACCESS_TOKEN_EXPIRY_TIME: String
ADMIN_SECRET: String
CUSTOM_ACCESS_TOKEN_SCRIPT: String
OLD_ADMIN_SECRET: String
diff --git a/server/handlers/authorize.go b/server/handlers/authorize.go
index cfb913f..b70c4de 100644
--- a/server/handlers/authorize.go
+++ b/server/handlers/authorize.go
@@ -5,6 +5,7 @@ import (
"net/http"
"strconv"
"strings"
+ "time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
@@ -279,7 +280,11 @@ func AuthorizeHandler() gin.HandlerFunc {
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
cookie.SetSession(gc, authToken.FingerPrintHash)
- expiresIn := int64(1800)
+
+ expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
+ if expiresIn <= 0 {
+ expiresIn = 1
+ }
// used of query mode
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
diff --git a/server/handlers/oauth_callback.go b/server/handlers/oauth_callback.go
index c9563a6..bfa4f00 100644
--- a/server/handlers/oauth_callback.go
+++ b/server/handlers/oauth_callback.go
@@ -157,7 +157,12 @@ func OAuthCallbackHandler() gin.HandlerFunc {
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
}
- expiresIn := int64(1800)
+
+ expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
+ if expiresIn <= 0 {
+ expiresIn = 1
+ }
+
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token
cookie.SetSession(c, authToken.FingerPrintHash)
diff --git a/server/handlers/token.go b/server/handlers/token.go
index 13abbb9..516aafe 100644
--- a/server/handlers/token.go
+++ b/server/handlers/token.go
@@ -5,6 +5,7 @@ import (
"encoding/base64"
"net/http"
"strings"
+ "time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
@@ -174,7 +175,11 @@ func TokenHandler() gin.HandlerFunc {
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
cookie.SetSession(gc, authToken.FingerPrintHash)
- expiresIn := int64(1800)
+ expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
+ if expiresIn <= 0 {
+ expiresIn = 1
+ }
+
res := map[string]interface{}{
"access_token": authToken.AccessToken.Token,
"id_token": authToken.IDToken.Token,
diff --git a/server/handlers/verify_email.go b/server/handlers/verify_email.go
index 80fe6ad..6333620 100644
--- a/server/handlers/verify_email.go
+++ b/server/handlers/verify_email.go
@@ -82,7 +82,12 @@ func VerifyEmailHandler() gin.HandlerFunc {
c.JSON(500, errorRes)
return
}
- expiresIn := int64(1800)
+
+ expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
+ if expiresIn <= 0 {
+ expiresIn = 1
+ }
+
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
cookie.SetSession(c, authToken.FingerPrintHash)
diff --git a/server/resolvers/env.go b/server/resolvers/env.go
index d56ea89..9ee3c60 100644
--- a/server/resolvers/env.go
+++ b/server/resolvers/env.go
@@ -27,6 +27,7 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
// get clone of store
store := envstore.EnvStoreObj.GetEnvStoreClone()
+ accessTokenExpiryTime := store.StringEnv[constants.EnvKeyAccessTokenExpiryTime]
adminSecret := store.StringEnv[constants.EnvKeyAdminSecret]
clientID := store.StringEnv[constants.EnvKeyClientID]
clientSecret := store.StringEnv[constants.EnvKeyClientSecret]
@@ -67,6 +68,7 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
organizationLogo := store.StringEnv[constants.EnvKeyOrganizationLogo]
res = &model.Env{
+ AccessTokenExpiryTime: &accessTokenExpiryTime,
AdminSecret: &adminSecret,
DatabaseName: databaseName,
DatabaseURL: databaseURL,
diff --git a/server/resolvers/login.go b/server/resolvers/login.go
index 57f8158..a93ca8d 100644
--- a/server/resolvers/login.go
+++ b/server/resolvers/login.go
@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"strings"
+ "time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
@@ -73,7 +74,11 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
return res, err
}
- expiresIn := int64(1800)
+ expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
+ if expiresIn <= 0 {
+ expiresIn = 1
+ }
+
res = &model.AuthResponse{
Message: `Logged in successfully`,
AccessToken: &authToken.AccessToken.Token,
diff --git a/server/resolvers/session.go b/server/resolvers/session.go
index e68fe07..22e7171 100644
--- a/server/resolvers/session.go
+++ b/server/resolvers/session.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
+ "time"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
@@ -73,7 +74,11 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
cookie.SetSession(gc, authToken.FingerPrintHash)
- expiresIn := int64(1800)
+ expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
+ if expiresIn <= 0 {
+ expiresIn = 1
+ }
+
res = &model.AuthResponse{
Message: `Session token refreshed`,
AccessToken: &authToken.AccessToken.Token,
diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go
index 76d7175..317416e 100644
--- a/server/resolvers/signup.go
+++ b/server/resolvers/signup.go
@@ -176,7 +176,10 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
cookie.SetSession(gc, authToken.FingerPrintHash)
go utils.SaveSessionInDB(gc, user.ID)
- expiresIn := int64(1800)
+ expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
+ if expiresIn <= 0 {
+ expiresIn = 1
+ }
res = &model.AuthResponse{
Message: `Signed up successfully.`,
diff --git a/server/resolvers/verify_email.go b/server/resolvers/verify_email.go
index fe13c9a..65e8494 100644
--- a/server/resolvers/verify_email.go
+++ b/server/resolvers/verify_email.go
@@ -64,7 +64,11 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
cookie.SetSession(gc, authToken.FingerPrintHash)
go utils.SaveSessionInDB(gc, user.ID)
- expiresIn := int64(1800)
+ expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
+ if expiresIn <= 0 {
+ expiresIn = 1
+ }
+
res = &model.AuthResponse{
Message: `Email verified successfully.`,
AccessToken: &authToken.AccessToken.Token,
diff --git a/server/token/auth_token.go b/server/token/auth_token.go
index 8714792..905df4a 100644
--- a/server/token/auth_token.go
+++ b/server/token/auth_token.go
@@ -130,7 +130,11 @@ func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonc
// CreateAccessToken util to create JWT token, based on
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce string) (string, int64, error) {
- expiryBound := time.Minute * 30
+ expiryBound, err := utils.ParseDurationInSeconds(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAccessTokenExpiryTime))
+ if err != nil {
+ expiryBound = time.Minute * 15
+ }
+
expiresAt := time.Now().Add(expiryBound).Unix()
customClaims := jwt.MapClaims{
@@ -282,7 +286,11 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
// CreateIDToken util to create JWT token, based on
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
func CreateIDToken(user models.User, roles []string, hostname, nonce string) (string, int64, error) {
- expiryBound := time.Minute * 30
+ expiryBound, err := utils.ParseDurationInSeconds(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAccessTokenExpiryTime))
+ if err != nil {
+ expiryBound = time.Minute * 15
+ }
+
expiresAt := time.Now().Add(expiryBound).Unix()
resUser := user.AsAPIUser()
diff --git a/server/utils/parser.go b/server/utils/parser.go
new file mode 100644
index 0000000..1b037c0
--- /dev/null
+++ b/server/utils/parser.go
@@ -0,0 +1,21 @@
+package utils
+
+import (
+ "errors"
+ "time"
+)
+
+// ParseDurationInSeconds parses input s, removes ms/us/ns and returns result duration
+func ParseDurationInSeconds(s string) (time.Duration, error) {
+ d, err := time.ParseDuration(s)
+ if err != nil {
+ return 0, err
+ }
+
+ d = d.Truncate(time.Second)
+ if d <= 0 {
+ return 0, errors.New(`duration must be greater than 0s`)
+ }
+
+ return d, nil
+}