Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d5f1c5a5eb | ||
![]() |
39947f1753 | ||
![]() |
4fa9f79c3f | ||
![]() |
fe73c2f6f8 | ||
![]() |
4a3e3633ea | ||
![]() |
dbbe36f6b5 | ||
![]() |
819dd57377 | ||
![]() |
044b025ba2 |
@@ -26,18 +26,16 @@
|
|||||||
- ✅ Sign-in / Sign-up with email ID and password
|
- ✅ Sign-in / Sign-up with email ID and password
|
||||||
- ✅ Secure session management
|
- ✅ Secure session management
|
||||||
- ✅ Email verification
|
- ✅ Email verification
|
||||||
|
- ✅ OAuth2 and OpenID compatible APIs
|
||||||
- ✅ APIs to update profile securely
|
- ✅ APIs to update profile securely
|
||||||
- ✅ Forgot password flow using email
|
- ✅ Forgot password flow using email
|
||||||
- ✅ Social logins (Google, Github, Facebook, more coming soon)
|
- ✅ Social logins (Google, Github, Facebook, more coming soon)
|
||||||
- ✅ Role-based access management
|
- ✅ Role-based access management
|
||||||
- ✅ Password-less login with email and magic link
|
- ✅ Password-less login with magic link login
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- Support more JWT encryption algorithms (Currently supporting HS256)
|
|
||||||
- 2 Factor authentication
|
- 2 Factor authentication
|
||||||
- Back office (Admin dashboard to manage user)
|
|
||||||
- Support more database
|
|
||||||
- VueJS SDK
|
- VueJS SDK
|
||||||
- Svelte SDK
|
- Svelte SDK
|
||||||
- React Native SDK
|
- React Native SDK
|
||||||
|
30
app/package-lock.json
generated
30
app/package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "latest",
|
"@authorizerdev/authorizer-react": "^0.17.0",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-js": {
|
"node_modules/@authorizerdev/authorizer-js": {
|
||||||
"version": "0.6.0",
|
"version": "0.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.10.0.tgz",
|
||||||
"integrity": "sha512-WbqeUmhQwLNlvk4ZYTptlbAIINh7aZPyTCVA/B0FE3EoPtx1tNOtkPtJOycrn0H0HyueeXQnBSCDxkvPAP65Bw==",
|
"integrity": "sha512-REM8FLD/Ej9gzA2zDGDAke6QFss33ubePlTDmLDmIYUuQmpHFlO5mCCS6nVsKkN7F/Bcwkmp+eUNQjkdGCaKLg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
},
|
},
|
||||||
@@ -35,11 +35,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-react": {
|
"node_modules/@authorizerdev/authorizer-react": {
|
||||||
"version": "0.11.0",
|
"version": "0.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.17.0.tgz",
|
||||||
"integrity": "sha512-VzSZvEB/t6N2ESn4O8c/+2hPUO7L4Iux8IBzXKrobKkoqRyb+u5TPZn0UWCOaoxIdiiZY+1Yq2A/H6q9LAqLGw==",
|
"integrity": "sha512-7WcNCU7hDFkVfFb8LcJXFwWiLYd8aY78z1AbNPxCa2Cw5G85PaRkzjKybP6h01ITVOHO6M03lLwPj8p6Sr6fEg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": "^0.6.0",
|
"@authorizerdev/authorizer-js": "^0.10.0",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
@@ -829,19 +829,19 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": {
|
"@authorizerdev/authorizer-js": {
|
||||||
"version": "0.6.0",
|
"version": "0.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.10.0.tgz",
|
||||||
"integrity": "sha512-WbqeUmhQwLNlvk4ZYTptlbAIINh7aZPyTCVA/B0FE3EoPtx1tNOtkPtJOycrn0H0HyueeXQnBSCDxkvPAP65Bw==",
|
"integrity": "sha512-REM8FLD/Ej9gzA2zDGDAke6QFss33ubePlTDmLDmIYUuQmpHFlO5mCCS6nVsKkN7F/Bcwkmp+eUNQjkdGCaKLg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@authorizerdev/authorizer-react": {
|
"@authorizerdev/authorizer-react": {
|
||||||
"version": "0.11.0",
|
"version": "0.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.17.0.tgz",
|
||||||
"integrity": "sha512-VzSZvEB/t6N2ESn4O8c/+2hPUO7L4Iux8IBzXKrobKkoqRyb+u5TPZn0UWCOaoxIdiiZY+1Yq2A/H6q9LAqLGw==",
|
"integrity": "sha512-7WcNCU7hDFkVfFb8LcJXFwWiLYd8aY78z1AbNPxCa2Cw5G85PaRkzjKybP6h01ITVOHO6M03lLwPj8p6Sr6fEg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@authorizerdev/authorizer-js": "^0.6.0",
|
"@authorizerdev/authorizer-js": "^0.10.0",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
"author": "Lakhan Samani",
|
"author": "Lakhan Samani",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "latest",
|
"@authorizerdev/authorizer-react": "^0.17.0",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
|
@@ -10,6 +10,9 @@ const queryClient = createClient({
|
|||||||
fetchOptions: () => {
|
fetchOptions: () => {
|
||||||
return {
|
return {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'x-authorizer-url': window.location.origin,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
requestPolicy: 'network-only',
|
requestPolicy: 'network-only',
|
||||||
|
@@ -2,6 +2,7 @@ export const LOGO_URL =
|
|||||||
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
|
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
|
||||||
|
|
||||||
export const TextInputType = {
|
export const TextInputType = {
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME',
|
||||||
CLIENT_ID: 'CLIENT_ID',
|
CLIENT_ID: 'CLIENT_ID',
|
||||||
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
|
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
|
||||||
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
|
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
|
||||||
@@ -125,4 +126,5 @@ export interface envVarTypes {
|
|||||||
DATABASE_NAME: string;
|
DATABASE_NAME: string;
|
||||||
DATABASE_TYPE: string;
|
DATABASE_TYPE: string;
|
||||||
DATABASE_URL: string;
|
DATABASE_URL: string;
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME: string;
|
||||||
}
|
}
|
||||||
|
@@ -53,6 +53,7 @@ export const EnvVariablesQuery = `
|
|||||||
DATABASE_NAME,
|
DATABASE_NAME,
|
||||||
DATABASE_TYPE,
|
DATABASE_TYPE,
|
||||||
DATABASE_URL,
|
DATABASE_URL,
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -85,6 +85,7 @@ export default function Environment() {
|
|||||||
DATABASE_NAME: '',
|
DATABASE_NAME: '',
|
||||||
DATABASE_TYPE: '',
|
DATABASE_TYPE: '',
|
||||||
DATABASE_URL: '',
|
DATABASE_URL: '',
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [fieldVisibility, setFieldVisibility] = React.useState<
|
const [fieldVisibility, setFieldVisibility] = React.useState<
|
||||||
@@ -600,11 +601,28 @@ export default function Environment() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
<Divider marginTop="2%" marginBottom="2%" />
|
||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
|
||||||
Custom Access Token Scripts
|
Access Token
|
||||||
</Text>
|
</Text>
|
||||||
<Stack spacing={6} padding="2% 0%">
|
<Stack spacing={6} padding="2% 0%">
|
||||||
<Flex>
|
<Flex>
|
||||||
<Center w="100%">
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Access Token Expiry Time:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Flex w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={envVariables}
|
||||||
|
setVariables={setEnvVariables}
|
||||||
|
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
|
||||||
|
placeholder="0h15m0s"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" direction="column">
|
||||||
|
<Text fontSize="sm">Custom Scripts:</Text>
|
||||||
|
<Text fontSize="sm">Used to add custom fields in ID token</Text>
|
||||||
|
</Flex>
|
||||||
|
<Flex w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
variables={envVariables}
|
variables={envVariables}
|
||||||
setVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
@@ -612,7 +630,7 @@ export default function Environment() {
|
|||||||
placeholder="Add script here"
|
placeholder="Add script here"
|
||||||
minH="25vh"
|
minH="25vh"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider marginTop="2%" marginBottom="2%" />
|
<Divider marginTop="2%" marginBottom="2%" />
|
||||||
|
@@ -16,11 +16,12 @@ const (
|
|||||||
// EnvKeyEnvPath key for cli arg variable ENV_PATH
|
// EnvKeyEnvPath key for cli arg variable ENV_PATH
|
||||||
EnvKeyEnvPath = "ENV_PATH"
|
EnvKeyEnvPath = "ENV_PATH"
|
||||||
// EnvKeyAuthorizerURL key for env variable AUTHORIZER_URL
|
// EnvKeyAuthorizerURL key for env variable AUTHORIZER_URL
|
||||||
// TODO: remove support AUTHORIZER_URL env
|
|
||||||
EnvKeyAuthorizerURL = "AUTHORIZER_URL"
|
EnvKeyAuthorizerURL = "AUTHORIZER_URL"
|
||||||
// EnvKeyPort key for env variable PORT
|
// EnvKeyPort key for env variable PORT
|
||||||
EnvKeyPort = "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 key for env variable ADMIN_SECRET
|
||||||
EnvKeyAdminSecret = "ADMIN_SECRET"
|
EnvKeyAdminSecret = "ADMIN_SECRET"
|
||||||
// EnvKeyDatabaseType key for env variable DATABASE_TYPE
|
// EnvKeyDatabaseType key for env variable DATABASE_TYPE
|
||||||
|
@@ -7,11 +7,11 @@ type VerificationRequest struct {
|
|||||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||||
Token string `gorm:"type:text" json:"token" bson:"token"`
|
Token string `gorm:"type:text" json:"token" bson:"token"`
|
||||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier"`
|
||||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||||
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email"`
|
||||||
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce"`
|
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce"`
|
||||||
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri"`
|
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
11
server/env/env.go
vendored
11
server/env/env.go
vendored
@@ -113,6 +113,10 @@ func InitAllEnv() error {
|
|||||||
envData.StringEnv[constants.EnvKeyAppURL] = os.Getenv(constants.EnvKeyAppURL)
|
envData.StringEnv[constants.EnvKeyAppURL] = os.Getenv(constants.EnvKeyAppURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if envData.StringEnv[constants.EnvKeyAuthorizerURL] == "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyAuthorizerURL] = os.Getenv(constants.EnvKeyAuthorizerURL)
|
||||||
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyPort] = os.Getenv(constants.EnvKeyPort)
|
envData.StringEnv[constants.EnvKeyPort] = os.Getenv(constants.EnvKeyPort)
|
||||||
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
if envData.StringEnv[constants.EnvKeyPort] == "" {
|
||||||
@@ -120,6 +124,13 @@ func InitAllEnv() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if envData.StringEnv[constants.EnvKeyAccessTokenExpiryTime] == "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyAccessTokenExpiryTime] = os.Getenv(constants.EnvKeyAccessTokenExpiryTime)
|
||||||
|
if envData.StringEnv[constants.EnvKeyAccessTokenExpiryTime] == "" {
|
||||||
|
envData.StringEnv[constants.EnvKeyAccessTokenExpiryTime] = "30m"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if envData.StringEnv[constants.EnvKeyAdminSecret] == "" {
|
if envData.StringEnv[constants.EnvKeyAdminSecret] == "" {
|
||||||
envData.StringEnv[constants.EnvKeyAdminSecret] = os.Getenv(constants.EnvKeyAdminSecret)
|
envData.StringEnv[constants.EnvKeyAdminSecret] = os.Getenv(constants.EnvKeyAdminSecret)
|
||||||
}
|
}
|
||||||
|
1
server/env/persist_env.go
vendored
1
server/env/persist_env.go
vendored
@@ -165,6 +165,7 @@ func PersistEnv() error {
|
|||||||
hasChanged = true
|
hasChanged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvStore(storeData)
|
envstore.EnvStoreObj.UpdateEnvStore(storeData)
|
||||||
jwk, err := crypto.GenerateJWKBasedOnEnv()
|
jwk, err := crypto.GenerateJWKBasedOnEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -53,6 +53,7 @@ type ComplexityRoot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Env struct {
|
Env struct {
|
||||||
|
AccessTokenExpiryTime func(childComplexity int) int
|
||||||
AdminSecret func(childComplexity int) int
|
AdminSecret func(childComplexity int) int
|
||||||
AllowedOrigins func(childComplexity int) int
|
AllowedOrigins func(childComplexity int) int
|
||||||
AppURL 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
|
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":
|
case "Env.ADMIN_SECRET":
|
||||||
if e.complexity.Env.AdminSecret == nil {
|
if e.complexity.Env.AdminSecret == nil {
|
||||||
break
|
break
|
||||||
@@ -1381,6 +1389,7 @@ type Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Env {
|
type Env {
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME: String
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
DATABASE_NAME: String!
|
DATABASE_NAME: String!
|
||||||
DATABASE_URL: String!
|
DATABASE_URL: String!
|
||||||
@@ -1432,6 +1441,7 @@ type GenerateJWTKeysResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input UpdateEnvInput {
|
input UpdateEnvInput {
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME: String
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
||||||
OLD_ADMIN_SECRET: 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)
|
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) {
|
func (ec *executionContext) _Env_ADMIN_SECRET(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -8093,6 +8135,14 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
|
|||||||
|
|
||||||
for k, v := range asMap {
|
for k, v := range asMap {
|
||||||
switch k {
|
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":
|
case "ADMIN_SECRET":
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -8711,6 +8761,8 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
|
|||||||
switch field.Name {
|
switch field.Name {
|
||||||
case "__typename":
|
case "__typename":
|
||||||
out.Values[i] = graphql.MarshalString("Env")
|
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":
|
case "ADMIN_SECRET":
|
||||||
out.Values[i] = ec._Env_ADMIN_SECRET(ctx, field, obj)
|
out.Values[i] = ec._Env_ADMIN_SECRET(ctx, field, obj)
|
||||||
case "DATABASE_NAME":
|
case "DATABASE_NAME":
|
||||||
|
@@ -24,6 +24,7 @@ type DeleteUserInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Env struct {
|
type Env struct {
|
||||||
|
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
|
||||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
AdminSecret *string `json:"ADMIN_SECRET"`
|
||||||
DatabaseName string `json:"DATABASE_NAME"`
|
DatabaseName string `json:"DATABASE_NAME"`
|
||||||
DatabaseURL string `json:"DATABASE_URL"`
|
DatabaseURL string `json:"DATABASE_URL"`
|
||||||
@@ -179,6 +180,7 @@ type UpdateAccessInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpdateEnvInput struct {
|
type UpdateEnvInput struct {
|
||||||
|
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
|
||||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
AdminSecret *string `json:"ADMIN_SECRET"`
|
||||||
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
||||||
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
|
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
|
||||||
|
@@ -87,6 +87,7 @@ type Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Env {
|
type Env {
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME: String
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
DATABASE_NAME: String!
|
DATABASE_NAME: String!
|
||||||
DATABASE_URL: String!
|
DATABASE_URL: String!
|
||||||
@@ -138,6 +139,7 @@ type GenerateJWTKeysResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input UpdateEnvInput {
|
input UpdateEnvInput {
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME: String
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
||||||
OLD_ADMIN_SECRET: String
|
OLD_ADMIN_SECRET: String
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
@@ -51,8 +51,6 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
gc.JSON(400, gin.H{"error": "invalid response mode"})
|
gc.JSON(400, gin.H{"error": "invalid response mode"})
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("=> redirect URI:", redirectURI)
|
|
||||||
fmt.Println("=> state:", state)
|
|
||||||
if redirectURI == "" {
|
if redirectURI == "" {
|
||||||
redirectURI = "/app"
|
redirectURI = "/app"
|
||||||
}
|
}
|
||||||
@@ -279,7 +277,11 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
expiresIn := int64(1800)
|
|
||||||
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
if expiresIn <= 0 {
|
||||||
|
expiresIn = 1
|
||||||
|
}
|
||||||
|
|
||||||
// used of query mode
|
// 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
|
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||||
|
@@ -157,7 +157,12 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, gin.H{"error": err.Error()})
|
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
|
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)
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
@@ -174,7 +175,11 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
if expiresIn <= 0 {
|
||||||
|
expiresIn = 1
|
||||||
|
}
|
||||||
|
|
||||||
res := map[string]interface{}{
|
res := map[string]interface{}{
|
||||||
"access_token": authToken.AccessToken.Token,
|
"access_token": authToken.AccessToken.Token,
|
||||||
"id_token": authToken.IDToken.Token,
|
"id_token": authToken.IDToken.Token,
|
||||||
|
@@ -82,7 +82,12 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
c.JSON(500, errorRes)
|
c.JSON(500, errorRes)
|
||||||
return
|
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
|
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)
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
|
@@ -27,6 +27,7 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
|
|||||||
|
|
||||||
// get clone of store
|
// get clone of store
|
||||||
store := envstore.EnvStoreObj.GetEnvStoreClone()
|
store := envstore.EnvStoreObj.GetEnvStoreClone()
|
||||||
|
accessTokenExpiryTime := store.StringEnv[constants.EnvKeyAccessTokenExpiryTime]
|
||||||
adminSecret := store.StringEnv[constants.EnvKeyAdminSecret]
|
adminSecret := store.StringEnv[constants.EnvKeyAdminSecret]
|
||||||
clientID := store.StringEnv[constants.EnvKeyClientID]
|
clientID := store.StringEnv[constants.EnvKeyClientID]
|
||||||
clientSecret := store.StringEnv[constants.EnvKeyClientSecret]
|
clientSecret := store.StringEnv[constants.EnvKeyClientSecret]
|
||||||
@@ -66,7 +67,12 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
|
|||||||
organizationName := store.StringEnv[constants.EnvKeyOrganizationName]
|
organizationName := store.StringEnv[constants.EnvKeyOrganizationName]
|
||||||
organizationLogo := store.StringEnv[constants.EnvKeyOrganizationLogo]
|
organizationLogo := store.StringEnv[constants.EnvKeyOrganizationLogo]
|
||||||
|
|
||||||
|
if accessTokenExpiryTime == "" {
|
||||||
|
accessTokenExpiryTime = "30m"
|
||||||
|
}
|
||||||
|
|
||||||
res = &model.Env{
|
res = &model.Env{
|
||||||
|
AccessTokenExpiryTime: &accessTokenExpiryTime,
|
||||||
AdminSecret: &adminSecret,
|
AdminSecret: &adminSecret,
|
||||||
DatabaseName: databaseName,
|
DatabaseName: databaseName,
|
||||||
DatabaseURL: databaseURL,
|
DatabaseURL: databaseURL,
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
@@ -73,7 +74,11 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
if expiresIn <= 0 {
|
||||||
|
expiresIn = 1
|
||||||
|
}
|
||||||
|
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Logged in successfully`,
|
Message: `Logged in successfully`,
|
||||||
AccessToken: &authToken.AccessToken.Token,
|
AccessToken: &authToken.AccessToken.Token,
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"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)
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
if expiresIn <= 0 {
|
||||||
|
expiresIn = 1
|
||||||
|
}
|
||||||
|
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Session token refreshed`,
|
Message: `Session token refreshed`,
|
||||||
AccessToken: &authToken.AccessToken.Token,
|
AccessToken: &authToken.AccessToken.Token,
|
||||||
|
@@ -176,7 +176,10 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
go utils.SaveSessionInDB(gc, user.ID)
|
go utils.SaveSessionInDB(gc, user.ID)
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
if expiresIn <= 0 {
|
||||||
|
expiresIn = 1
|
||||||
|
}
|
||||||
|
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Signed up successfully.`,
|
Message: `Signed up successfully.`,
|
||||||
|
@@ -64,7 +64,11 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
|||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
go utils.SaveSessionInDB(gc, user.ID)
|
go utils.SaveSessionInDB(gc, user.ID)
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
if expiresIn <= 0 {
|
||||||
|
expiresIn = 1
|
||||||
|
}
|
||||||
|
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Email verified successfully.`,
|
Message: `Email verified successfully.`,
|
||||||
AccessToken: &authToken.AccessToken.Token,
|
AccessToken: &authToken.AccessToken.Token,
|
||||||
|
@@ -130,7 +130,11 @@ func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonc
|
|||||||
// CreateAccessToken util to create JWT token, based on
|
// CreateAccessToken util to create JWT token, based on
|
||||||
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||||
func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce string) (string, int64, error) {
|
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 * 30
|
||||||
|
}
|
||||||
|
|
||||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||||
|
|
||||||
customClaims := jwt.MapClaims{
|
customClaims := jwt.MapClaims{
|
||||||
@@ -282,7 +286,11 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
|
|||||||
// CreateIDToken util to create JWT token, based on
|
// CreateIDToken util to create JWT token, based on
|
||||||
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||||
func CreateIDToken(user models.User, roles []string, hostname, nonce string) (string, int64, error) {
|
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 * 30
|
||||||
|
}
|
||||||
|
|
||||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||||
|
|
||||||
resUser := user.AsAPIUser()
|
resUser := user.AsAPIUser()
|
||||||
|
21
server/utils/parser.go
Normal file
21
server/utils/parser.go
Normal file
@@ -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
|
||||||
|
}
|
@@ -10,7 +10,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetHost returns hostname from request context
|
// GetHost returns hostname from request context
|
||||||
|
// if X-Authorizer-URL header is set it is given highest priority
|
||||||
|
// if EnvKeyAuthorizerURL is set it is given second highest priority.
|
||||||
|
// if above 2 are not set the requesting host name is used
|
||||||
func GetHost(c *gin.Context) string {
|
func GetHost(c *gin.Context) string {
|
||||||
|
authorizerURL := c.Request.Header.Get("X-Authorizer-URL")
|
||||||
|
if authorizerURL != "" {
|
||||||
|
return authorizerURL
|
||||||
|
}
|
||||||
|
|
||||||
|
authorizerURL = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL)
|
||||||
|
if authorizerURL != "" {
|
||||||
|
return authorizerURL
|
||||||
|
}
|
||||||
|
|
||||||
scheme := c.Request.Header.Get("X-Forwarded-Proto")
|
scheme := c.Request.Header.Get("X-Forwarded-Proto")
|
||||||
if scheme != "https" {
|
if scheme != "https" {
|
||||||
scheme = "http"
|
scheme = "http"
|
||||||
|
Reference in New Issue
Block a user