Skip to content

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-hanna committed Jul 23, 2018
2 parents b3aba6b + fda79af commit 3f596e6
Show file tree
Hide file tree
Showing 22 changed files with 322 additions and 225 deletions.
Binary file added .README.md.swp
Binary file not shown.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.DS_Store
.DS_Store

coverate.out
Binary file added .gitignore.swp
Binary file not shown.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ When request are made to protected endpoint, this CSRF secret needs to be sent t
## Cookies or Bearer Tokens?
This API is setup to either use cookies (default) or bearer tokens. To use bearer tokens, set the BearerTokens option equal to true in the config settings.

When using bearer tokens, you'll need to include the auth and refresh jwt's (along with your csrf secret) in each request. Include them in the request headers. The keys can be defined in the auth options, but default to "X-Auth-Token" and "X-Refresh-Token", respectively. See the bearerTokens example for sample code of both.
When using bearer tokens, you'll need to include the auth and (optionally [the]) refresh jwt's (along with your csrf secret) in each request. Include them in the request headers. The keys can be defined in the auth options, but default to "X-Auth-Token" and "X-Refresh-Token", respectively. See the bearerTokens example for sample code of both.

Ideally, if using bearer tokens, they should be stored in a location that cannot be accessed with javascript. You want to be able to separate your csrf secret from your jwt's. If using web, I suggest using cookies. If using mobile, store these in a secure manner!

Expand Down Expand Up @@ -559,7 +559,7 @@ func main() {
~~~ bash
$ cd jwt && go test -coverprofile=test/coverage.out

coverage: 83.4% of statements
coverage: 84.7% of statements

$ go tool cover -html=test/coverage.out
~~~
Expand Down Expand Up @@ -587,4 +587,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
~~~
~~~
4 changes: 2 additions & 2 deletions examples/bearerTokens/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ func main() {
SigningMethodString: "RS256",
PrivateKeyLocation: "keys/app.rsa", // `$ openssl genrsa -out app.rsa 2048`
PublicKeyLocation: "keys/app.rsa.pub", // `$ openssl rsa -in app.rsa -pubout > app.rsa.pub`
RefreshTokenValidTime: 10 * time.Second,
AuthTokenValidTime: 5 * time.Second,
RefreshTokenValidTime: 10 * time.Minute,
AuthTokenValidTime: 5 * time.Minute,
Debug: true,
BearerTokens: true,
})
Expand Down
4 changes: 2 additions & 2 deletions examples/bearerTokens/templates/templateFiles/login.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
var headers = {
"Authorization": "Bearer " + window.localStorage.getItem("X-CSRF-Token"),
"X-Auth-Token": window.localStorage.getItem("X-Auth-Token"),
"X-Refresh-Token": window.localStorage.getItem("X-Refresh-Token")
// "X-Refresh-Token": window.localStorage.getItem("X-Refresh-Token")
};

$.ajax({
Expand Down Expand Up @@ -134,4 +134,4 @@
});
</script>
</body>
</html>
</html>
9 changes: 9 additions & 0 deletions examples/detailed/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions examples/detailed/Gopkg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true


[prune]
go-tests = true
unused-packages = true
70 changes: 48 additions & 22 deletions jwt/auth-utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ func (a *Auth) extractTokenStringsFromReq(r *http.Request) (string, string, *jwt
return r.Header.Get(a.options.AuthTokenName), r.Header.Get(a.options.RefreshTokenName), nil
}

var (
authCookieValue string
refreshCookieValue string
)

AuthCookie, authErr := r.Cookie(a.options.AuthTokenName)
if authErr == http.ErrNoCookie {
a.myLog("Unauthorized attempt! No auth cookie")
Expand All @@ -28,15 +33,19 @@ func (a *Auth) extractTokenStringsFromReq(r *http.Request) (string, string, *jwt
}

RefreshCookie, refreshErr := r.Cookie(a.options.RefreshTokenName)
if refreshErr == http.ErrNoCookie {
a.myLog("Unauthorized attempt! No refresh cookie")
return "", "", newJwtError(errors.New("No refresh cookie"), 401)
} else if refreshErr != nil {
if refreshErr != nil && refreshErr != http.ErrNoCookie {
a.myLog(refreshErr)
return "", "", newJwtError(errors.New("Internal Server Error"), 500)
}

return AuthCookie.Value, RefreshCookie.Value, nil
if AuthCookie != nil {
authCookieValue = AuthCookie.Value
}
if RefreshCookie != nil {
refreshCookieValue = RefreshCookie.Value
}

return authCookieValue, refreshCookieValue, nil
}

func (a *Auth) extractCsrfStringFromReq(r *http.Request) (string, *jwtError) {
Expand All @@ -62,19 +71,30 @@ func (a *Auth) extractCsrfStringFromReq(r *http.Request) (string, *jwtError) {
}

func (a *Auth) setCredentialsOnResponseWriter(w http.ResponseWriter, c *credentials) *jwtError {
var (
refreshTokenString string
refreshTokenClaims *ClaimsType
)

authTokenString, err := c.AuthToken.Token.SignedString(a.signKey)
if err != nil {
return newJwtError(err, 500)
}
refreshTokenString, err := c.RefreshToken.Token.SignedString(a.signKey)
if err != nil {
return newJwtError(err, 500)
if c.RefreshToken != nil && c.RefreshToken.Token != nil {
log.Println(c.RefreshToken)
log.Println(c.RefreshToken.Token)
refreshTokenString, err = c.RefreshToken.Token.SignedString(a.signKey)
if err != nil {
return newJwtError(err, 500)
}
}

if a.options.BearerTokens {
// tokens are not in cookies
setHeader(w, a.options.AuthTokenName, authTokenString)
setHeader(w, a.options.RefreshTokenName, refreshTokenString)
if refreshTokenString != "" {
setHeader(w, a.options.RefreshTokenName, refreshTokenString)
}
} else {
// tokens are in cookies
// note: don't use an "Expires" in auth cookies bc browsers won't send expired cookies?
Expand All @@ -88,33 +108,39 @@ func (a *Auth) setCredentialsOnResponseWriter(w http.ResponseWriter, c *credenti
}
http.SetCookie(w, &authCookie)

refreshCookie := http.Cookie{
Name: a.options.RefreshTokenName,
Value: refreshTokenString,
Expires: time.Now().Add(a.options.RefreshTokenValidTime),
Path: "/",
HttpOnly: true,
Secure: !a.options.IsDevEnv,
if refreshTokenString != "" {
refreshCookie := http.Cookie{
Name: a.options.RefreshTokenName,
Value: refreshTokenString,
Expires: time.Now().Add(a.options.RefreshTokenValidTime),
Path: "/",
HttpOnly: true,
Secure: !a.options.IsDevEnv,
}
http.SetCookie(w, &refreshCookie)
}
http.SetCookie(w, &refreshCookie)
}

authTokenClaims, ok := c.AuthToken.Token.Claims.(*ClaimsType)
if !ok {
a.myLog("Cannot read auth token claims")
return newJwtError(errors.New("Cannot read token claims"), 500)
}
refreshTokenClaims, ok := c.RefreshToken.Token.Claims.(*ClaimsType)
if !ok {
a.myLog("Cannot read refresh token claims")
return newJwtError(errors.New("Cannot read token claims"), 500)
if c.RefreshToken != nil && c.RefreshToken.Token != nil {
refreshTokenClaims, ok = c.RefreshToken.Token.Claims.(*ClaimsType)
if !ok {
a.myLog("Cannot read refresh token claims")
return newJwtError(errors.New("Cannot read token claims"), 500)
}
}

w.Header().Set(a.options.CSRFTokenName, c.CsrfString)
// note @adam-hanna: this may not be correct when using a sep auth server?
// bc it checks the request?
w.Header().Set("Auth-Expiry", strconv.FormatInt(authTokenClaims.StandardClaims.ExpiresAt, 10))
w.Header().Set("Refresh-Expiry", strconv.FormatInt(refreshTokenClaims.StandardClaims.ExpiresAt, 10))
if refreshTokenClaims != nil {
w.Header().Set("Refresh-Expiry", strconv.FormatInt(refreshTokenClaims.StandardClaims.ExpiresAt, 10))
}

return nil
}
Expand Down
14 changes: 10 additions & 4 deletions jwt/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ func (a *Auth) buildCredentialsFromClaims(c *credentials, claims *ClaimsType) *j

func (a *Auth) buildCredentialsFromStrings(csrfString string, authTokenString string, refreshTokenString string, c *credentials) *jwtError {
// check inputs
if csrfString == "" || authTokenString == "" || refreshTokenString == "" {
return newJwtError(errors.New("Invalid inputs to build credentials. Inputs cannot be blank"), 401)
}
//if csrfString == "" || authTokenString == "" || refreshTokenString == "" {
//return newJwtError(errors.New("Invalid inputs to build credentials. Inputs cannot be blank"), 401)
//}

// inputs are good
c.CsrfString = csrfString
Expand All @@ -84,7 +84,9 @@ func (a *Auth) buildCredentialsFromStrings(csrfString string, authTokenString st
// Also, tokens that have expired will throw err?
c.AuthToken = c.buildTokenWithClaimsFromString(authTokenString, a.verifyKey, a.options.AuthTokenValidTime)

c.RefreshToken = c.buildTokenWithClaimsFromString(refreshTokenString, a.verifyKey, a.options.RefreshTokenValidTime)
if refreshTokenString != "" {
c.RefreshToken = c.buildTokenWithClaimsFromString(refreshTokenString, a.verifyKey, a.options.RefreshTokenValidTime)
}

return nil
}
Expand Down Expand Up @@ -119,6 +121,10 @@ func generateNewCsrfString() (string, *jwtError) {
}

func (c *credentials) updateAuthTokenFromRefreshToken() *jwtError {
if c.RefreshToken == nil || c.RefreshToken.Token == nil {
return newJwtError(errors.New("Refresh token is invalid. Cannot refresh auth token."), 401)
}

refreshTokenClaims, ok := c.RefreshToken.Token.Claims.(*ClaimsType)
if !ok {
return newJwtError(errors.New("Cannot read token claims"), 500)
Expand Down
Loading

0 comments on commit 3f596e6

Please sign in to comment.