add auth/jwt/refresh-token example as requested at #1635

This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-09-16 13:57:11 +03:00
parent 64f95a59b4
commit 85b5453ae1
No known key found for this signature in database
GPG Key ID: 5DBE766BD26A54E7
3 changed files with 146 additions and 0 deletions

View File

@ -181,6 +181,7 @@
* [Basic Authentication](auth/basicauth/main.go) * [Basic Authentication](auth/basicauth/main.go)
* [CORS](auth/cors) * [CORS](auth/cors)
* [JWT](auth/jwt/main.go) * [JWT](auth/jwt/main.go)
* [Refresh Token](auth/jwt/refresh-token/main.go)
* [JWT (community edition)](https://github.com/iris-contrib/middleware/tree/v12/jwt/_example/main.go) * [JWT (community edition)](https://github.com/iris-contrib/middleware/tree/v12/jwt/_example/main.go)
* [OAUth2](auth/goth/main.go) * [OAUth2](auth/goth/main.go)
* [Manage Permissions](auth/permissions/main.go) * [Manage Permissions](auth/permissions/main.go)

View File

@ -0,0 +1,139 @@
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
// UserClaims a custom claims structure. You can just use jwt.Claims too.
type UserClaims struct {
jwt.Claims
Username string
}
// TokenPair holds the access token and refresh token response.
type TokenPair struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
func main() {
app := iris.New()
// Access token, short-live.
accessJWT := jwt.HMAC(15*time.Minute, "secret", "itsa16bytesecret")
// Refresh token, long-live. Important: Give different secret keys(!)
refreshJWT := jwt.HMAC(1*time.Hour, "other secret", "other16bytesecre")
// On refresh token, we extract it only from a request body
// of JSON, e.g. {"refresh_token": $token }.
// You can also do it manually in the handler level though.
refreshJWT.Extractors = []jwt.TokenExtractor{
jwt.FromJSON("refresh_token"),
}
// Generate access and refresh tokens and send to the client.
app.Get("/authenticate", func(ctx iris.Context) {
tokenPair, err := generateTokenPair(accessJWT, refreshJWT)
if err != nil {
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.JSON(tokenPair)
})
app.Get("/refresh", func(ctx iris.Context) {
// Manual (if jwt.FromJSON missing):
// var payload = struct {
// RefreshToken string `json:"refresh_token"`
// }{}
//
// err := ctx.ReadJSON(&payload)
// if err != nil {
// ctx.StatusCode(iris.StatusBadRequest)
// return
// }
//
// j.VerifyTokenString(ctx, payload.RefreshToken, &claims)
var claims jwt.Claims
if err := refreshJWT.VerifyToken(ctx, &claims); err != nil {
ctx.Application().Logger().Warnf("verify refresh token: %v", err)
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
userID := claims.Subject
if userID == "" {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
// Simulate a database call against our jwt subject.
if userID != "53afcf05-38a3-43c3-82af-8bbbe0e4a149" {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
// All OK, re-generate the new pair and send to client.
tokenPair, err := generateTokenPair(accessJWT, refreshJWT)
if err != nil {
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.JSON(tokenPair)
})
app.Get("/", func(ctx iris.Context) {
var claims UserClaims
if err := accessJWT.VerifyToken(ctx, &claims); err != nil {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
ctx.Writef("Username: %s\nExpires at: %s\n", claims.Username, claims.Expiry.Time())
})
// http://localhost:8080 (401)
// http://localhost:8080/authenticate (200) (response JSON {access_token, refresh_token})
// http://localhost:8080?token={access_token} (200)
// http://localhost:8080?token={refresh_token} (401)
// http://localhost:8080/refresh (request JSON{refresh_token = {refresh_token}}) (200) (response JSON {access_token, refresh_token})
app.Listen(":8080")
}
func generateTokenPair(accessJWT, refreshJWT *jwt.JWT) (TokenPair, error) {
standardClaims := jwt.Claims{Issuer: "an-issuer", Audience: jwt.Audience{"an-audience"}}
customClaims := UserClaims{
Claims: accessJWT.Expiry(standardClaims),
Username: "kataras",
}
accessToken, err := accessJWT.Token(customClaims)
if err != nil {
return TokenPair{}, err
}
// At refresh tokens you don't need any custom claims.
refreshClaims := refreshJWT.Expiry(jwt.Claims{
ID: "refresh_kataras",
// For example, the User ID,
// this is nessecary to check against the database
// if the user still exist or has credentials to access our page.
Subject: "53afcf05-38a3-43c3-82af-8bbbe0e4a149",
})
refreshToken, err := refreshJWT.Token(refreshClaims)
if err != nil {
return TokenPair{}, err
}
return TokenPair{
AccessToken: accessToken,
RefreshToken: refreshToken,
}, nil
}

View File

@ -476,6 +476,12 @@ func (j *JWT) VerifyToken(ctx *context.Context, claimsPtr interface{}) error {
} }
} }
return j.VerifyTokenString(ctx, token, claimsPtr)
}
// VerifyTokenString verifies and unmarshals an extracted token to "claimsPtr" destination.
// The Context is required when the claims validator needs it, otherwise can be nil.
func (j *JWT) VerifyTokenString(ctx *context.Context, token string, claimsPtr interface{}) error {
if token == "" { if token == "" {
return ErrMissing return ErrMissing
} }