feat: persist encrypted env

This commit is contained in:
Lakhan Samani
2021-12-31 13:52:10 +05:30
parent d9c40057e6
commit e35d0cbcd6
41 changed files with 751 additions and 298 deletions

View File

@@ -17,7 +17,7 @@ import (
)
func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
expiryBound := time.Hour
if tokenType == enum.RefreshToken {
// expires in 1 year
@@ -32,11 +32,11 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
json.Unmarshal(userBytes, &userMap)
customClaims := jwt.MapClaims{
"exp": expiresAt,
"iat": time.Now().Unix(),
"token_type": tokenType.String(),
"allowed_roles": strings.Split(user.Roles, ","),
constants.JWT_ROLE_CLAIM: roles,
"exp": expiresAt,
"iat": time.Now().Unix(),
"token_type": tokenType.String(),
"allowed_roles": strings.Split(user.Roles, ","),
constants.EnvData.JWT_ROLE_CLAIM: roles,
}
for k, v := range userMap {
@@ -77,7 +77,7 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
t.Claims = customClaims
token, err := t.SignedString([]byte(constants.JWT_SECRET))
token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET))
if err != nil {
return "", 0, err
}
@@ -89,7 +89,6 @@ func GetAuthToken(gc *gin.Context) (string, error) {
token, err := GetCookie(gc)
if err != nil || token == "" {
// try to check in auth header for cookie
log.Println("cookie not found checking headers")
auth := gc.Request.Header.Get("Authorization")
if auth == "" {
return "", fmt.Errorf(`unauthorized`)
@@ -105,7 +104,7 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) {
claims := jwt.MapClaims{}
_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(constants.JWT_SECRET), nil
return []byte(constants.EnvData.JWT_SECRET), nil
})
if err != nil {
return res, err
@@ -126,7 +125,7 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) {
}
func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
expiryBound := time.Hour
if tokenType == enum.RefreshToken {
// expires in 1 year
@@ -146,9 +145,23 @@ func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int
t.Claims = customClaims
token, err := t.SignedString([]byte(constants.JWT_SECRET))
token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET))
if err != nil {
return "", 0, err
}
return token, expiresAt, nil
}
func GetAdminAuthToken(gc *gin.Context) (string, error) {
token, err := GetAdminCookie(gc)
if err != nil || token == "" {
// try to check in auth header for cookie
auth := gc.Request.Header.Get("Authorization")
if auth == "" {
return "", fmt.Errorf(`unauthorized`)
}
token = strings.TrimPrefix(auth, "Bearer ")
}
return token, nil
}

View File

@@ -10,21 +10,21 @@ import (
func SetCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL)
domain := GetDomainName(constants.AUTHORIZER_URL)
host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
domain := GetDomainName(constants.EnvData.AUTHORIZER_URL)
if domain != "localhost" {
domain = "." + domain
}
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
gc.SetCookie(constants.COOKIE_NAME+"-client", token, 3600, "/", domain, secure, httpOnly)
gc.SetCookie(constants.EnvData.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
gc.SetCookie(constants.EnvData.COOKIE_NAME+"-client", token, 3600, "/", domain, secure, httpOnly)
}
func GetCookie(gc *gin.Context) (string, error) {
cookie, err := gc.Request.Cookie(constants.COOKIE_NAME)
cookie, err := gc.Request.Cookie(constants.EnvData.COOKIE_NAME)
if err != nil {
cookie, err = gc.Request.Cookie(constants.COOKIE_NAME + "-client")
cookie, err = gc.Request.Cookie(constants.EnvData.COOKIE_NAME + "-client")
if err != nil {
return "", err
}
@@ -37,29 +37,37 @@ func DeleteCookie(gc *gin.Context) {
secure := true
httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL)
domain := GetDomainName(constants.AUTHORIZER_URL)
host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
domain := GetDomainName(constants.EnvData.AUTHORIZER_URL)
if domain != "localhost" {
domain = "." + domain
}
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
gc.SetCookie(constants.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly)
gc.SetCookie(constants.EnvData.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
gc.SetCookie(constants.EnvData.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly)
}
func SetAdminCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL)
host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
gc.SetCookie("authorizer-admin", token, 3600, "/", host, secure, httpOnly)
gc.SetCookie(constants.EnvData.ADMIN_COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
}
func GetAdminCookie(gc *gin.Context) (string, error) {
cookie, err := gc.Request.Cookie(constants.EnvData.ADMIN_COOKIE_NAME)
if err != nil {
return "", err
}
return cookie.Value, nil
}
func DeleteAdminCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL)
host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
gc.SetCookie("authorizer-admin", "", -1, "/", host, secure, httpOnly)
gc.SetCookie(constants.EnvData.ADMIN_COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
}

83
server/utils/crypto.go Normal file
View File

@@ -0,0 +1,83 @@
package utils
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"io"
"github.com/authorizerdev/authorizer/server/constants"
)
func EncryptB64(text string) string {
return base64.StdEncoding.EncodeToString([]byte(text))
}
func DecryptB64(s string) (string, error) {
data, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return "", err
}
return string(data), nil
}
func EncryptAES(text []byte) ([]byte, error) {
key := []byte(constants.EnvData.ENCRYPTION_KEY)
c, err := aes.NewCipher(key)
var res []byte
if err != nil {
return res, err
}
// gcm or Galois/Counter Mode, is a mode of operation
// for symmetric key cryptographic block ciphers
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
gcm, err := cipher.NewGCM(c)
if err != nil {
return res, err
}
// creates a new byte array the size of the nonce
// which must be passed to Seal
nonce := make([]byte, gcm.NonceSize())
// populates our nonce with a cryptographically secure
// random sequence
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return res, err
}
// here we encrypt our text using the Seal function
// Seal encrypts and authenticates plaintext, authenticates the
// additional data and appends the result to dst, returning the updated
// slice. The nonce must be NonceSize() bytes long and unique for all
// time, for a given key.
return gcm.Seal(nonce, nonce, text, nil), nil
}
func DecryptAES(ciphertext []byte) ([]byte, error) {
key := []byte(constants.EnvData.ENCRYPTION_KEY)
c, err := aes.NewCipher(key)
var res []byte
if err != nil {
return res, err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return res, err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return res, err
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return res, err
}
return plaintext, nil
}

View File

@@ -100,7 +100,7 @@ func SendVerificationMail(toEmail, token string) error {
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
</body>
</html>
`, constants.ORGANIZATION_LOGO, constants.ORGANIZATION_NAME, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token)
`, constants.EnvData.ORGANIZATION_LOGO, constants.EnvData.ORGANIZATION_NAME, constants.EnvData.AUTHORIZER_URL+"/verify_email"+"?token="+token)
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
return sender.SendMail(Receiver, Subject, bodyMessage)
@@ -108,8 +108,8 @@ func SendVerificationMail(toEmail, token string) error {
// SendForgotPasswordMail to send verification email
func SendForgotPasswordMail(toEmail, token, host string) error {
if constants.RESET_PASSWORD_URL == "" {
constants.RESET_PASSWORD_URL = constants.AUTHORIZER_URL + "/app/reset-password"
if constants.EnvData.RESET_PASSWORD_URL == "" {
constants.EnvData.RESET_PASSWORD_URL = constants.EnvData.AUTHORIZER_URL + "/app/reset-password"
}
sender := email.NewSender()
@@ -204,7 +204,7 @@ func SendForgotPasswordMail(toEmail, token, host string) error {
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
</body>
</html>
`, constants.ORGANIZATION_LOGO, toEmail, constants.RESET_PASSWORD_URL+"?token="+token)
`, constants.EnvData.ORGANIZATION_LOGO, toEmail, constants.EnvData.RESET_PASSWORD_URL+"?token="+token)
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)

View File

@@ -9,12 +9,12 @@ import (
// version,
func GetMetaInfo() model.Meta {
return model.Meta{
Version: constants.VERSION,
IsGoogleLoginEnabled: constants.GOOGLE_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "",
IsGithubLoginEnabled: constants.GITHUB_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "",
IsFacebookLoginEnabled: constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "",
IsBasicAuthenticationEnabled: !constants.DISABLE_BASIC_AUTHENTICATION,
IsEmailVerificationEnabled: !constants.DISABLE_EMAIL_VERIFICATION,
IsMagicLinkLoginEnabled: !constants.DISABLE_MAGIC_LINK_LOGIN,
Version: constants.EnvData.VERSION,
IsGoogleLoginEnabled: constants.EnvData.GOOGLE_CLIENT_ID != "" && constants.EnvData.GOOGLE_CLIENT_SECRET != "",
IsGithubLoginEnabled: constants.EnvData.GITHUB_CLIENT_ID != "" && constants.EnvData.GOOGLE_CLIENT_SECRET != "",
IsFacebookLoginEnabled: constants.EnvData.FACEBOOK_CLIENT_ID != "" && constants.EnvData.FACEBOOK_CLIENT_SECRET != "",
IsBasicAuthenticationEnabled: !constants.EnvData.DISABLE_BASIC_AUTHENTICATION,
IsEmailVerificationEnabled: !constants.EnvData.DISABLE_EMAIL_VERIFICATION,
IsMagicLinkLoginEnabled: !constants.EnvData.DISABLE_MAGIC_LINK_LOGIN,
}
}

View File

@@ -16,7 +16,7 @@ func IsValidEmail(email string) bool {
}
func IsValidOrigin(url string) bool {
if len(constants.ALLOWED_ORIGINS) == 1 && constants.ALLOWED_ORIGINS[0] == "*" {
if len(constants.EnvData.ALLOWED_ORIGINS) == 1 && constants.EnvData.ALLOWED_ORIGINS[0] == "*" {
return true
}
@@ -24,7 +24,7 @@ func IsValidOrigin(url string) bool {
hostName, port := GetHostParts(url)
currentOrigin := hostName + ":" + port
for _, origin := range constants.ALLOWED_ORIGINS {
for _, origin := range constants.EnvData.ALLOWED_ORIGINS {
replacedString := origin
// if has regex whitelisted domains
if strings.Contains(origin, "*") {
@@ -50,12 +50,17 @@ func IsValidOrigin(url string) bool {
}
func IsSuperAdmin(gc *gin.Context) bool {
secret := gc.Request.Header.Get("x-authorizer-admin-secret")
if secret == "" {
return false
token, err := GetAdminAuthToken(gc)
if err != nil {
secret := gc.Request.Header.Get("x-authorizer-admin-secret")
if secret == "" {
return false
}
return secret == constants.EnvData.ADMIN_SECRET
}
return secret == constants.ADMIN_SECRET
return token != ""
}
func IsValidRoles(userRoles []string, roles []string) bool {

View File

@@ -20,23 +20,23 @@ type CustomClaim struct {
}
func CreateVerificationToken(email string, tokenType string) (string, error) {
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
t.Claims = &CustomClaim{
&jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
},
tokenType,
UserInfo{Email: email, Host: constants.AUTHORIZER_URL, RedirectURL: constants.APP_URL},
UserInfo{Email: email, Host: constants.EnvData.AUTHORIZER_URL, RedirectURL: constants.EnvData.APP_URL},
}
return t.SignedString([]byte(constants.JWT_SECRET))
return t.SignedString([]byte(constants.EnvData.JWT_SECRET))
}
func VerifyVerificationToken(token string) (*CustomClaim, error) {
claims := &CustomClaim{}
_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(constants.JWT_SECRET), nil
return []byte(constants.EnvData.JWT_SECRET), nil
})
if err != nil {
return claims, err