-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b986c37
commit ac01c17
Showing
5 changed files
with
272 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,14 +47,14 @@ Binary will available in folder "./build/". Run it and you can access through ht | |
|
||
AWS Secrets Manager UI tool uses AWS configuration credential to authenticate requests. | ||
|
||
### Credential file | ||
|
||
More detail: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html | ||
|
||
### Credential environment variables (recommend) | ||
|
||
More detail: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html | ||
|
||
### Credential file | ||
|
||
More detail: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html | ||
|
||
### Minimum Permission | ||
|
||
- Easy policy name: SecretsManagerReadWrite : https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_available-policies.html | ||
|
@@ -64,6 +64,28 @@ More detail: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envv | |
|
||
## Authentication | ||
|
||
### AWS Cognito authentication | ||
|
||
Configurations to enable for AWS Cognito | ||
|
||
- `AUTH_ENABLED=true` | ||
- `AUTH_TYPE=aws_cognito_auth2` | ||
- `AWS_COGNITO_APP_NAME=administrator`: Get from AWS Cognito App configuration | ||
- `AWS_COGNITO_REGION=eu-north-1`: Get from AWS Cognito App configuration | ||
- `AWS_COGNITO_CLIENT_ID={client_id}`: Get from AWS Cognito App configuration | ||
- `AWS_COGNITO_CLIENT_SECRET={secrets}`: Get from AWS Cognito App configuration | ||
- `AWS_COGNITO_REDIRECT_URL=http://localhost:30301/cognito/auth`: Redirect URL you want AWS cognito call back | ||
- `[email protected]`: Limit accepted users to login. Empty = all | ||
- `AWS_COGNITO_LOGIN_URL=https://administrator.auth.eu-north-1.amazoncognito.com/login?...`: Get from AWS Cognito App configuration | ||
|
||
AWS Cognito App configurations | ||
|
||
![aws_cognito_1](https://user-images.githubusercontent.com/1828895/139128226-5f5b0068-a54e-49b6-80d1-36261476e7d0.png) | ||
|
||
![aws_cognito_2](https://user-images.githubusercontent.com/1828895/139128230-bbecb312-3f2e-4fdf-887a-fc089c184ea4.png) | ||
|
||
### Basic authenticationBLED | ||
|
||
Default, AWS Secrets manager UI disable authentication. | ||
|
||
AWS Secrets manager supports basic auth through two variable environments, in order enable it, try with 2 variable environments: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package actions | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"os" | ||
"strings" | ||
) | ||
|
||
func GetAWSCognitoAppName() string { | ||
return os.Getenv("AWS_COGNITO_APP_NAME") | ||
} | ||
|
||
func GetAWSCognitoRegion() string { | ||
return os.Getenv("AWS_COGNITO_REGION") | ||
} | ||
|
||
func GetAWSCognitoClientID() string { | ||
return os.Getenv("AWS_COGNITO_CLIENT_ID") | ||
} | ||
|
||
func GetAWSCognitoClientSecret() string { | ||
return os.Getenv("AWS_COGNITO_CLIENT_SECRET") | ||
} | ||
|
||
func GetAWSCognitoHost() string { | ||
return fmt.Sprintf( | ||
"https://%s.auth.%s.amazoncognito.com", | ||
GetAWSCognitoAppName(), | ||
GetAWSCognitoRegion(), | ||
) | ||
} | ||
|
||
type CognitoToken struct { | ||
AccessToken *string `json:"access_token"` | ||
TokenType *string `json:"token_type"` | ||
IDToken *string `json:"id_token"` | ||
ExpiresIn *int64 `json:"expires_in"` | ||
} | ||
|
||
func GetAWSCognitoAccessToken(authCode string) (CognitoToken, error) { | ||
urlStr := GetAWSCognitoHost() + "/oauth2/token" | ||
request := url.Values{} | ||
request.Set("grant_type", "authorization_code") | ||
request.Set("client_id", GetAWSCognitoClientID()) | ||
request.Set("code", authCode) | ||
request.Set("redirect_uri", os.Getenv("AWS_COGNITO_REDIRECT_URL")) | ||
r, err := http.NewRequest(http.MethodPost, urlStr, strings.NewReader(request.Encode())) | ||
if err != nil { | ||
return CognitoToken{}, err | ||
} | ||
r.Header.Add("Content-Type", "application/x-www-form-urlencoded") | ||
r.Header.Add("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", GetAWSCognitoClientID(), GetAWSCognitoClientSecret()))))) | ||
client := &http.Client{} | ||
resp, err := client.Do(r) | ||
if err != nil { | ||
return CognitoToken{}, err | ||
} | ||
body, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return CognitoToken{}, err | ||
} | ||
if resp.StatusCode < 200 || resp.StatusCode > 299 { | ||
return CognitoToken{}, errors.New(string(body)) | ||
} | ||
|
||
var token CognitoToken | ||
if err := json.Unmarshal(body, &token); err != nil { | ||
return CognitoToken{}, err | ||
} | ||
|
||
return token, nil | ||
} | ||
|
||
type CognitoUserInfo struct { | ||
Sub string `json:"sub"` | ||
Name string `json:"name"` | ||
GivenName string `json:"given_name"` | ||
FamilyName string `json:"family_name"` | ||
PreferredUsername string `json:"preferred_username"` | ||
Email string `json:"email"` | ||
} | ||
|
||
func (c CognitoUserInfo) IsValidEmail() (bool, error) { | ||
emails := os.Getenv("AWS_COGNITO_ALLOWED_EMAILS") | ||
if len(emails) == 0 { | ||
return true, nil | ||
} | ||
for _, email := range strings.Split(emails, ",") { | ||
if email == c.Email { | ||
return true, nil | ||
} | ||
} | ||
return false, fmt.Errorf("%s doesn't have permission to access system", c.Email) | ||
} | ||
|
||
func GetAWSCognitoUserInfo(accessToken string) (CognitoUserInfo, error) { | ||
urlStr := GetAWSCognitoHost() + "/oauth2/userInfo" | ||
r, _ := http.NewRequest(http.MethodGet, urlStr, nil) | ||
r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken)) | ||
client := &http.Client{} | ||
resp, err := client.Do(r) | ||
if err != nil { | ||
return CognitoUserInfo{}, err | ||
} | ||
body, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return CognitoUserInfo{}, err | ||
} | ||
if resp.StatusCode < 200 || resp.StatusCode > 299 { | ||
return CognitoUserInfo{}, errors.New(string(body)) | ||
} | ||
|
||
var userInfo CognitoUserInfo | ||
if err := json.Unmarshal(body, &userInfo); err != nil { | ||
return CognitoUserInfo{}, err | ||
} | ||
return userInfo, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package auth | ||
|
||
import ( | ||
"net/http" | ||
"os" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/labstack/echo/v4" | ||
"github.com/labstack/gommon/log" | ||
) | ||
|
||
var awsCognitoLoginUsers *sync.Map | ||
|
||
func GetAWSCognitoLoginUsers() *sync.Map { | ||
if awsCognitoLoginUsers == nil { | ||
awsCognitoLoginUsers = &sync.Map{} | ||
} | ||
return awsCognitoLoginUsers | ||
} | ||
|
||
func AWSCognitoMiddleware(skipPathes []string) echo.MiddlewareFunc { | ||
return func(next echo.HandlerFunc) echo.HandlerFunc { | ||
return func(c echo.Context) error { | ||
for _, path := range skipPathes { | ||
if strings.HasPrefix(c.Request().URL.Path, path) { | ||
return next(c) | ||
} | ||
} | ||
cookie, err := c.Cookie("aws_cognito_token") | ||
if err != nil { | ||
return awsCognitoError(c, err.Error()) | ||
} | ||
if cookie == nil { | ||
return awsCognitoError(c, "Fail to load cookie") | ||
} | ||
|
||
_, valid := GetAWSCognitoLoginUsers().Load(cookie.Value) | ||
if !valid { | ||
return awsCognitoError(c, "invalid or expired token") | ||
} | ||
|
||
return next(c) | ||
} | ||
} | ||
} | ||
|
||
func awsCognitoError(c echo.Context, errMsg string) error { | ||
loginURL := os.Getenv("AWS_COGNITO_LOGIN_URL") | ||
if len(loginURL) == 0 { | ||
return echo.NewHTTPError(http.StatusUnauthorized, errMsg) | ||
} | ||
log.Warn("Unauthentication: ", errMsg) | ||
return c.Redirect(http.StatusTemporaryRedirect, loginURL) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package routes | ||
|
||
import ( | ||
"crypto/sha256" | ||
"encoding/base64" | ||
"fmt" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/labstack/echo/v4" | ||
|
||
"github.com/ledongthuc/awssecretsmanagerui/server/actions" | ||
"github.com/ledongthuc/awssecretsmanagerui/server/auth" | ||
) | ||
|
||
func SetupAWSCognitoRoute(g *echo.Group) { | ||
g.GET("/auth", func(c echo.Context) error { | ||
authCode := c.QueryParam("code") | ||
if len(authCode) == 0 { | ||
return c.JSON(http.StatusBadRequest, "missing auth code") | ||
} | ||
token, err := actions.GetAWSCognitoAccessToken(authCode) | ||
if err != nil { | ||
return c.JSON(http.StatusInternalServerError, err.Error()) | ||
} | ||
|
||
if token.AccessToken == nil { | ||
return c.JSON(http.StatusInternalServerError, fmt.Errorf("Missing access token")) | ||
} | ||
userInfo, err := actions.GetAWSCognitoUserInfo(*token.AccessToken) | ||
if err != nil { | ||
return c.JSON(http.StatusInternalServerError, err.Error()) | ||
} | ||
|
||
if valid, err := userInfo.IsValidEmail(); !valid { | ||
return c.JSON(http.StatusBadRequest, err.Error()) | ||
} | ||
|
||
h := sha256.New() | ||
h.Write([]byte(*token.AccessToken)) | ||
clientToken := base64.StdEncoding.EncodeToString(h.Sum(nil)) | ||
|
||
cookie := new(http.Cookie) | ||
cookie.Name = "aws_cognito_token" | ||
cookie.Value = clientToken | ||
cookie.Path = "/" | ||
cookie.Expires = time.Now().Add(24 * time.Hour) | ||
c.SetCookie(cookie) | ||
|
||
auth.GetAWSCognitoLoginUsers().Store(clientToken, struct{}{}) | ||
return c.HTML(http.StatusOK, `<html> | ||
<head><script>location.href = "/";</script></head> | ||
<body></body> | ||
</html>`) | ||
// return c.Redirect(http.StatusTemporaryRedirect, "/") | ||
}) | ||
} |