Compare commits

..

15 Commits
1.1.4 ... 1.1.8

Author SHA1 Message Date
Lakhan Samani
aa6601e62c fix: same site cookie 2022-09-28 18:30:30 +05:30
Lakhan Samani
d8ea0c656f Merge pull request #247 from authorizerdev/fix/same-site-cookie
fix(server): use sameSite as lax by default for app cookie
2022-09-28 11:18:03 +05:30
Lakhan Samani
f5323e0eec fix(server): update comments for host & cookies 2022-09-28 10:36:56 +05:30
Lakhan Samani
b1bc7b5370 fix(server): set default app cookie to lax mode 2022-09-28 09:51:04 +05:30
Lakhan Samani
536fd87c3c fix: debug log 2022-09-27 06:45:38 +05:30
Lakhan Samani
f8c96a9fee Merge pull request #244 from authorizerdev/fix/remove-user-verification-request-when-deleted
fix: remove entries from otp + verification when user is deleted
2022-09-27 06:44:22 +05:30
Lakhan Samani
837fc781de fix: remove entries from otp + verification when user is deleted
Resolves #234
2022-09-27 00:27:36 +05:30
Lakhan Samani
640bb8c9ed chore: bump app/authorizer-react 1.1.2 2022-09-27 00:07:52 +05:30
Lakhan Samani
d9bba0bbe7 Merge pull request #243 from authorizerdev/fix/bool-env-vars-secure-cookie
fix: app & admin cookie secure variable type while persisting info
2022-09-27 00:03:31 +05:30
Lakhan Samani
f91ec1880f fix: app & admin cookie secure variable type while persisting info
Resolves #241
2022-09-27 00:01:38 +05:30
Lakhan Samani
19e2153379 Update README.md 2022-09-15 12:24:47 +05:30
Lakhan Samani
221009bf0a Merge pull request #229 from ruessej/main
feat: Add a option to disable httpOnly cookies
2022-09-15 11:22:27 +05:30
ruessej
6085c2d535 Fix incorrect type 2022-09-14 12:24:19 +02:00
Jerebtw
8e0c5e4380 Make the default value true 2022-09-14 11:56:48 +02:00
ruessej
195bd1bc6a Add a option to disable httpOnly cookies 2022-09-12 14:37:42 +02:00
13 changed files with 153 additions and 35 deletions

View File

@@ -9,13 +9,11 @@
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports 11+ databases including [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [YugaByte](https://www.yugabyte.com/), [MariaDB](https://mariadb.org/), [PlanetScale](https://planetscale.com/), [CassandraDB](https://cassandra.apache.org/_/index.html), [ScyllaDB](https://www.scylladb.com/), [MongoDB](https://mongodb.com/), [ArangoDB](https://www.arangodb.com/)).
## Table of contents
For more information check:
- [Introduction](#introduction)
- [Getting Started](#getting-started)
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
- [Docs](http://docs.authorizer.dev/)
- [Join Community](https://discord.gg/Zv2D5h6kkK)
- [Discord Community](https://discord.gg/Zv2D5h6kkK)
- [Contributing Guide](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
# Introduction
@@ -38,13 +36,13 @@
## Roadmap
- VueJS SDK
- Svelte SDK
- [VueJS SDK](https://github.com/authorizerdev/authorizer-vue)
- [Svelte SDK](https://github.com/authorizerdev/authorizer-svelte)
- [Golang SDK](https://github.com/authorizerdev/authorizer-go)
- React Native SDK
- Flutter SDK
- Android Native SDK
- iOS native SDK
- Golang SDK
- Python SDK
- PHP SDK
- WordPress plugin

26
app/package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@authorizerdev/authorizer-react": "^1.1.0",
"@authorizerdev/authorizer-react": "^1.1.2",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17",
@@ -37,9 +37,9 @@
}
},
"node_modules/@authorizerdev/authorizer-react": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.0.tgz",
"integrity": "sha512-8ooyBREFI6ohHApVOPQitFr7T0w0SlpEVZruvU9oqa8OQ77UBxLQh+PCRKKPw7FeQRdCdh/VQyl17W7Xphp1NA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.2.tgz",
"integrity": "sha512-uBmuKnOVX8gp8CEUuGJuz04ep+8qMEzJXWd5leEGKYMIgolHpu/lOinnMUXhjh8YL3pA4+EhvB+hQXxUX+rRHQ==",
"dependencies": {
"@authorizerdev/authorizer-js": "^1.1.0",
"final-form": "^4.20.2",
@@ -469,9 +469,9 @@
}
},
"node_modules/final-form": {
"version": "4.20.6",
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.6.tgz",
"integrity": "sha512-fCdwIj49KOaFfDRlXB57Eo+GghIMZQWrA9TakQI3C9uQxHwaFHXqZSNRlUdfnQmNNeySwGOaGPZCvjy58hyv4w==",
"version": "4.20.4",
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.4.tgz",
"integrity": "sha512-hyoOVVilPLpkTvgi+FSJkFZrh0Yhy4BhE6lk/NiBwrF4aRV8/ykKEyXYvQH/pfUbRkOosvpESYouFb+FscsLrw==",
"dependencies": {
"@babel/runtime": "^7.10.0"
},
@@ -868,9 +868,9 @@
}
},
"@authorizerdev/authorizer-react": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.0.tgz",
"integrity": "sha512-8ooyBREFI6ohHApVOPQitFr7T0w0SlpEVZruvU9oqa8OQ77UBxLQh+PCRKKPw7FeQRdCdh/VQyl17W7Xphp1NA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.2.tgz",
"integrity": "sha512-uBmuKnOVX8gp8CEUuGJuz04ep+8qMEzJXWd5leEGKYMIgolHpu/lOinnMUXhjh8YL3pA4+EhvB+hQXxUX+rRHQ==",
"requires": {
"@authorizerdev/authorizer-js": "^1.1.0",
"final-form": "^4.20.2",
@@ -1216,9 +1216,9 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"final-form": {
"version": "4.20.6",
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.6.tgz",
"integrity": "sha512-fCdwIj49KOaFfDRlXB57Eo+GghIMZQWrA9TakQI3C9uQxHwaFHXqZSNRlUdfnQmNNeySwGOaGPZCvjy58hyv4w==",
"version": "4.20.4",
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.4.tgz",
"integrity": "sha512-hyoOVVilPLpkTvgi+FSJkFZrh0Yhy4BhE6lk/NiBwrF4aRV8/ykKEyXYvQH/pfUbRkOosvpESYouFb+FscsLrw==",
"requires": {
"@babel/runtime": "^7.10.0"
}

View File

@@ -11,7 +11,7 @@
"author": "Lakhan Samani",
"license": "ISC",
"dependencies": {
"@authorizerdev/authorizer-react": "^1.1.0",
"@authorizerdev/authorizer-react": "^1.1.2",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17",

View File

@@ -49,6 +49,10 @@ const (
EnvKeySenderEmail = "SENDER_EMAIL"
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED"
// EnvKeyAppCookieSecure key for env variable APP_COOKIE_SECURE
EnvKeyAppCookieSecure = "APP_COOKIE_SECURE"
// EnvKeyAdminCookieSecure key for env variable ADMIN_COOKIE_SECURE
EnvKeyAdminCookieSecure = "ADMIN_COOKIE_SECURE"
// EnvKeyJwtType key for env variable JWT_TYPE
EnvKeyJwtType = "JWT_TYPE"
// EnvKeyJwtSecret key for env variable JWT_SECRET

View File

@@ -14,3 +14,14 @@ const (
// VerificationTypeOTP is the otp verification type
VerificationTypeOTP = "verify_otp"
)
var (
// VerificationTypes is slice of all verification types
VerificationTypes = []string{
VerificationTypeBasicAuthSignup,
VerificationTypeMagicLinkLogin,
VerificationTypeUpdateEmail,
VerificationTypeForgotPassword,
VerificationTypeInviteMember,
}
)

View File

@@ -3,15 +3,24 @@ package cookie
import (
"net/url"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers"
"github.com/gin-gonic/gin"
)
// SetAdminCookie sets the admin cookie in the response
func SetAdminCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
adminCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAdminCookieSecure)
if err != nil {
log.Debug("Error while getting admin cookie secure from env variable: %v", err)
adminCookieSecure = true
}
secure := adminCookieSecure
httpOnly := adminCookieSecure
hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname)
gc.SetCookie(constants.AdminCookieName, token, 3600, "/", host, secure, httpOnly)
@@ -35,8 +44,14 @@ func GetAdminCookie(gc *gin.Context) (string, error) {
// DeleteAdminCookie sets the response cookie to empty
func DeleteAdminCookie(gc *gin.Context) {
secure := true
httpOnly := true
adminCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAdminCookieSecure)
if err != nil {
log.Debug("Error while getting admin cookie secure from env variable: %v", err)
adminCookieSecure = true
}
secure := adminCookieSecure
httpOnly := adminCookieSecure
hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname)
gc.SetCookie(constants.AdminCookieName, "", -1, "/", host, secure, httpOnly)

View File

@@ -4,15 +4,24 @@ import (
"net/http"
"net/url"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers"
"github.com/gin-gonic/gin"
)
// SetSession sets the session cookie in the response
func SetSession(gc *gin.Context, sessionID string) {
secure := true
httpOnly := true
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
if err != nil {
log.Debug("Error while getting app cookie secure from env variable: %v", err)
appCookieSecure = true
}
secure := appCookieSecure
httpOnly := appCookieSecure
hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname)
@@ -20,18 +29,33 @@ func SetSession(gc *gin.Context, sessionID string) {
domain = "." + domain
}
// Use sameSite = lax by default
// Since app cookie can come from cross site it becomes important to set this in lax mode.
// Example person using custom UI on their app domain and making request to authorizer domain.
// For more information check:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// https://github.com/gin-gonic/gin/blob/master/context.go#L86
// TODO add ability to sameSite = none / strict from dashboard
if !appCookieSecure {
gc.SetSameSite(http.SameSiteLaxMode)
}
// TODO allow configuring from dashboard
year := 60 * 60 * 24 * 365
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.AppCookieName+"_session", sessionID, year, "/", host, secure, httpOnly)
gc.SetCookie(constants.AppCookieName+"_session_domain", sessionID, year, "/", domain, secure, httpOnly)
}
// DeleteSession sets session cookies to expire
func DeleteSession(gc *gin.Context) {
secure := true
httpOnly := true
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
if err != nil {
log.Debug("Error while getting app cookie secure from env variable: %v", err)
appCookieSecure = true
}
secure := appCookieSecure
httpOnly := appCookieSecure
hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname)

36
server/env/env.go vendored
View File

@@ -79,6 +79,8 @@ func InitAllEnv() error {
osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo)
// os bool vars
osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure)
osAdminCookieSecure := os.Getenv(constants.EnvKeyAdminCookieSecure)
osDisableBasicAuthentication := os.Getenv(constants.EnvKeyDisableBasicAuthentication)
osDisableEmailVerification := os.Getenv(constants.EnvKeyDisableEmailVerification)
osDisableMagicLinkLogin := os.Getenv(constants.EnvKeyDisableMagicLinkLogin)
@@ -417,6 +419,40 @@ func InitAllEnv() error {
envData[constants.EnvKeyOrganizationLogo] = osOrganizationLogo
}
if _, ok := envData[constants.EnvKeyAppCookieSecure]; !ok {
if osAppCookieSecure == "" {
envData[constants.EnvKeyAppCookieSecure] = true
} else {
envData[constants.EnvKeyAppCookieSecure] = osAppCookieSecure == "true"
}
}
if osAppCookieSecure != "" {
boolValue, err := strconv.ParseBool(osAppCookieSecure)
if err != nil {
return err
}
if boolValue != envData[constants.EnvKeyAppCookieSecure].(bool) {
envData[constants.EnvKeyAppCookieSecure] = boolValue
}
}
if _, ok := envData[constants.EnvKeyAdminCookieSecure]; !ok {
if osAdminCookieSecure == "" {
envData[constants.EnvKeyAdminCookieSecure] = true
} else {
envData[constants.EnvKeyAdminCookieSecure] = osAdminCookieSecure == "true"
}
}
if osAdminCookieSecure != "" {
boolValue, err := strconv.ParseBool(osAdminCookieSecure)
if err != nil {
return err
}
if boolValue != envData[constants.EnvKeyAdminCookieSecure].(bool) {
envData[constants.EnvKeyAdminCookieSecure] = boolValue
}
}
if _, ok := envData[constants.EnvKeyDisableBasicAuthentication]; !ok {
envData[constants.EnvKeyDisableBasicAuthentication] = osDisableBasicAuthentication == "true"
}

View File

@@ -201,7 +201,7 @@ func PersistEnv() error {
envValue := strings.TrimSpace(os.Getenv(key))
if envValue != "" {
switch key {
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication:
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure:
if envValueBool, err := strconv.ParseBool(envValue); err == nil {
if value.(bool) != envValueBool {
storeData[key] = envValueBool

View File

@@ -34,6 +34,8 @@ func InitMemStore() error {
constants.EnvKeyIsEmailServiceEnabled: false,
constants.EnvKeyEnforceMultiFactorAuthentication: false,
constants.EnvKeyDisableMultiFactorAuthentication: false,
constants.EnvKeyAppCookieSecure: true,
constants.EnvKeyAdminCookieSecure: true,
}
requiredEnvs := RequiredEnvStoreObj.GetRequiredEnv()

View File

@@ -161,7 +161,7 @@ func (c *provider) GetEnvStore() (map[string]interface{}, error) {
return nil, err
}
for key, value := range data {
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication {
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure {
boolValue, err := strconv.ParseBool(value)
if err != nil {
return res, err

View File

@@ -11,8 +11,8 @@ import (
)
// 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 EnvKeyAuthorizerURL is set it is given highest priority.
// if X-Authorizer-URL header 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 {
authorizerURL, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL)

View File

@@ -50,6 +50,34 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
}
go func() {
// delete otp for given email
otp, err := db.Provider.GetOTPByEmail(ctx, user.Email)
if err != nil {
log.Infof("No OTP found for email (%s): %v", user.Email, err)
// continue
} else {
err := db.Provider.DeleteOTP(ctx, otp)
if err != nil {
log.Debugf("Failed to delete otp for given email (%s): %v", user.Email, err)
// continue
}
}
// delete verification requests for given email
for _, vt := range constants.VerificationTypes {
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, user.Email, vt)
if err != nil {
log.Infof("No verification verification request found for email: %s, verification_request_type: %s. %v", user.Email, vt, err)
// continue
} else {
err := db.Provider.DeleteVerificationRequest(ctx, verificationRequest)
if err != nil {
log.Debugf("Failed to DeleteVerificationRequest for email: %s, verification_request_type: %s. %v", user.Email, vt, err)
// continue
}
}
}
memorystore.Provider.DeleteAllUserSessions(user.ID)
utils.RegisterEvent(ctx, constants.UserDeletedWebhookEvent, "", user)
}()