JWT: add token audience
a token released for API audience cannot be used for web pages and vice-versa
This commit is contained in:
parent
78bf808322
commit
4f609cfa30
6 changed files with 299 additions and 184 deletions
|
@ -173,13 +173,13 @@ func changeAdminPassword(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func doChangeAdminPassword(r *http.Request, currentPassword, newPassword, confirmNewPassword string) error {
|
||||
if currentPassword == "" || newPassword == "" || confirmNewPassword == "" {
|
||||
return dataprovider.NewValidationError("Please provide the current password and the new one two times")
|
||||
return dataprovider.NewValidationError("please provide the current password and the new one two times")
|
||||
}
|
||||
if newPassword != confirmNewPassword {
|
||||
return dataprovider.NewValidationError("The two password fields do not match")
|
||||
return dataprovider.NewValidationError("the two password fields do not match")
|
||||
}
|
||||
if currentPassword == newPassword {
|
||||
return dataprovider.NewValidationError("The new password must be different from the current one")
|
||||
return dataprovider.NewValidationError("the new password must be different from the current one")
|
||||
}
|
||||
claims, err := getTokenClaims(r)
|
||||
if err != nil {
|
||||
|
@ -191,7 +191,7 @@ func doChangeAdminPassword(r *http.Request, currentPassword, newPassword, confir
|
|||
}
|
||||
match, err := admin.CheckPassword(currentPassword)
|
||||
if !match || err != nil {
|
||||
return dataprovider.NewValidationError("Current password does not match")
|
||||
return dataprovider.NewValidationError("current password does not match")
|
||||
}
|
||||
|
||||
admin.Password = newPassword
|
||||
|
|
|
@ -12,6 +12,13 @@ import (
|
|||
"github.com/drakkan/sftpgo/utils"
|
||||
)
|
||||
|
||||
type tokenAudience = string
|
||||
|
||||
const (
|
||||
tokenAudienceWeb tokenAudience = "Web"
|
||||
tokenAudienceAPI tokenAudience = "API"
|
||||
)
|
||||
|
||||
const (
|
||||
claimUsernameKey = "username"
|
||||
claimPermissionsKey = "permissions"
|
||||
|
@ -87,13 +94,14 @@ func (c *jwtTokenClaims) hasPerm(perm string) bool {
|
|||
return utils.IsStringInSlice(perm, c.Permissions)
|
||||
}
|
||||
|
||||
func (c *jwtTokenClaims) createTokenResponse(tokenAuth *jwtauth.JWTAuth) (map[string]interface{}, error) {
|
||||
func (c *jwtTokenClaims) createTokenResponse(tokenAuth *jwtauth.JWTAuth, audience tokenAudience) (map[string]interface{}, error) {
|
||||
claims := c.asMap()
|
||||
now := time.Now().UTC()
|
||||
|
||||
claims[jwt.JwtIDKey] = xid.New().String()
|
||||
claims[jwt.NotBeforeKey] = now.Add(-30 * time.Second)
|
||||
claims[jwt.ExpirationKey] = now.Add(tokenDuration)
|
||||
claims[jwt.AudienceKey] = audience
|
||||
|
||||
token, tokenString, err := tokenAuth.Encode(claims)
|
||||
if err != nil {
|
||||
|
@ -108,7 +116,7 @@ func (c *jwtTokenClaims) createTokenResponse(tokenAuth *jwtauth.JWTAuth) (map[st
|
|||
}
|
||||
|
||||
func (c *jwtTokenClaims) createAndSetCookie(w http.ResponseWriter, r *http.Request, tokenAuth *jwtauth.JWTAuth) error {
|
||||
resp, err := c.createTokenResponse(tokenAuth)
|
||||
resp, err := c.createTokenResponse(tokenAuth, tokenAudienceWeb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -352,7 +352,7 @@ func TestUpdateWebAdminInvalidClaims(t *testing.T) {
|
|||
Permissions: admin.Permissions,
|
||||
Signature: admin.GetSignature(),
|
||||
}
|
||||
token, err := c.createTokenResponse(server.tokenAuth)
|
||||
token, err := c.createTokenResponse(server.tokenAuth, tokenAudienceWeb)
|
||||
assert.NoError(t, err)
|
||||
|
||||
form := make(url.Values)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/lestrrat-go/jwx/jwt"
|
||||
|
||||
"github.com/drakkan/sftpgo/logger"
|
||||
"github.com/drakkan/sftpgo/utils"
|
||||
)
|
||||
|
||||
type ctxKeyConnAddr int
|
||||
|
@ -37,6 +38,11 @@ func jwtAuthenticator(next http.Handler) http.Handler {
|
|||
sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if !utils.IsStringInSlice(tokenAudienceAPI, token.Audience()) {
|
||||
logger.Debug(logSender, "", "the token audience is not valid")
|
||||
sendAPIResponse(w, r, nil, "Your token audience is not valid", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if isTokenInvalidated(r) {
|
||||
logger.Debug(logSender, "", "the token has been invalidated")
|
||||
sendAPIResponse(w, r, nil, "Your token is no longer valid", http.StatusUnauthorized)
|
||||
|
@ -64,6 +70,11 @@ func jwtAuthenticatorWeb(next http.Handler) http.Handler {
|
|||
http.Redirect(w, r, webLoginPath, http.StatusFound)
|
||||
return
|
||||
}
|
||||
if !utils.IsStringInSlice(tokenAudienceWeb, token.Audience()) {
|
||||
logger.Debug(logSender, "", "the token audience is not valid")
|
||||
http.Redirect(w, r, webLoginPath, http.StatusFound)
|
||||
return
|
||||
}
|
||||
if isTokenInvalidated(r) {
|
||||
logger.Debug(logSender, "", "the token has been invalidated")
|
||||
http.Redirect(w, r, webLoginPath, http.StatusFound)
|
||||
|
|
|
@ -175,7 +175,7 @@ func (s *httpdServer) checkAddrAndSendToken(w http.ResponseWriter, r *http.Reque
|
|||
Signature: admin.GetSignature(),
|
||||
}
|
||||
|
||||
resp, err := c.createTokenResponse(s.tokenAuth)
|
||||
resp, err := c.createTokenResponse(s.tokenAuth, tokenAudienceAPI)
|
||||
|
||||
if err != nil {
|
||||
sendAPIResponse(w, r, err, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
|
|
Loading…
Add table
Reference in a new issue