mirror of
https://github.com/kataras/iris.git
synced 2025-01-24 03:01:03 +01:00
133 lines
4.5 KiB
Go
133 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/kataras/iris/v12"
|
|
"github.com/kataras/iris/v12/middleware/jwt"
|
|
)
|
|
|
|
// UserClaims a custom access claims structure.
|
|
type UserClaims struct {
|
|
// In order to separate refresh and access tokens on validation level:
|
|
// - Set a different Issuer, with a field of: Issuer string `json:"iss"`
|
|
// - Set the Iris JWT's json tag option "required" on an access token field,
|
|
// e.g. Username string `json:"username,required"`
|
|
// - Let the middleware validate the correct one based on the given MaxAge,
|
|
// which should be different between refresh and max age (refersh should be bigger)
|
|
// by setting the `jwt.ExpectRefreshToken` on Verify/VerifyToken/VerifyTokenString
|
|
// (see `refreshToken` function below)
|
|
ID string `json:"user_id"`
|
|
Username string `json:"username"`
|
|
}
|
|
|
|
// For refresh token, we will just use the jwt.Claims
|
|
// structure which contains the standard JWT fields.
|
|
|
|
func main() {
|
|
app := iris.New()
|
|
|
|
j := jwt.HMAC(15*time.Minute, "secret", "itsa16bytesecret")
|
|
|
|
app.Get("/authenticate", func(ctx iris.Context) {
|
|
generateTokenPair(ctx, j)
|
|
})
|
|
|
|
app.Get("/refresh", func(ctx iris.Context) {
|
|
refreshToken(ctx, j)
|
|
})
|
|
|
|
protectedAPI := app.Party("/protected")
|
|
{
|
|
protectedAPI.Use(j.Verify(func() interface{} {
|
|
return new(UserClaims)
|
|
})) // OR j.VerifyToken(ctx, &claims, jwt.MeetRequirements(&UserClaims{}))
|
|
|
|
protectedAPI.Get("/", func(ctx iris.Context) {
|
|
// Get token info, even if our UserClaims does not embed those
|
|
// through GetTokenInfo:
|
|
expiresAt := jwt.GetTokenInfo(ctx).Claims.Expiry.Time()
|
|
// Get your custom JWT claims through Get,
|
|
// which is a shortcut of GetTokenInfo(ctx).Value:
|
|
claims := jwt.Get(ctx).(*UserClaims)
|
|
|
|
ctx.Writef("Username: %s\nExpires at: %s\n", claims.Username, expiresAt)
|
|
})
|
|
}
|
|
|
|
// http://localhost:8080/protected (401)
|
|
// http://localhost:8080/authenticate (200) (response JSON {access_token, refresh_token})
|
|
// http://localhost:8080/protected?token={access_token} (200)
|
|
// http://localhost:8080/protected?token={refresh_token} (401)
|
|
// http://localhost:8080/refresh?token={refresh_token}
|
|
// OR http://localhost:8080/refresh (request JSON{refresh_token = {refresh_token}}) (200) (response JSON {access_token, refresh_token})
|
|
// OR http://localhost:8080/refresh (request PLAIN TEXT of {refresh_token}) (200) (response JSON {access_token, refresh_token})
|
|
// http://localhost:8080/refresh?token={access_token} (401)
|
|
app.Listen(":8080")
|
|
}
|
|
|
|
func generateTokenPair(ctx iris.Context, j *jwt.JWT) {
|
|
// Simulate a user...
|
|
userID := "53afcf05-38a3-43c3-82af-8bbbe0e4a149"
|
|
|
|
// Map the current user with the refresh token,
|
|
// so we make sure, on refresh route, that this refresh token owns
|
|
// to that user before re-generate.
|
|
refresh := jwt.Claims{Subject: userID}
|
|
|
|
access := UserClaims{
|
|
ID: userID,
|
|
Username: "kataras",
|
|
}
|
|
|
|
// Generates a Token Pair, long-live for refresh tokens, e.g. 1 hour.
|
|
// Second argument is the refresh claims and,
|
|
// the last one is the access token's claims.
|
|
tokenPair, err := j.TokenPair(1*time.Hour, refresh, access)
|
|
if err != nil {
|
|
ctx.Application().Logger().Debugf("token pair: %v", err)
|
|
ctx.StopWithStatus(iris.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Send the generated token pair to the client.
|
|
// The tokenPair looks like: {"access_token": $token, "refresh_token": $token}
|
|
ctx.JSON(tokenPair)
|
|
}
|
|
|
|
func refreshToken(ctx iris.Context, j *jwt.JWT) {
|
|
/*
|
|
We could pass a jwt.Claims pointer as the second argument,
|
|
but we don't have to because the method already returns
|
|
the standard JWT claims information back to us:
|
|
refresh, err := VerifyRefreshToken(ctx, nil)
|
|
*/
|
|
|
|
// Assuming you have access to the current user, e.g. sessions.
|
|
//
|
|
// Simulate a database call against our jwt subject
|
|
// to make sure that this refresh token is a pair generated by this user.
|
|
// * Note: You can remove the ExpectSubject and do this validation later on by yourself.
|
|
currentUserID := "53afcf05-38a3-43c3-82af-8bbbe0e4a149"
|
|
|
|
// Verify the refresh token, which its subject MUST match the "currentUserID".
|
|
_, err := j.VerifyRefreshToken(ctx, nil, jwt.ExpectSubject(currentUserID))
|
|
if err != nil {
|
|
ctx.Application().Logger().Debugf("verify refresh token: %v", err)
|
|
ctx.StatusCode(iris.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
/* Custom validation checks can be performed after Verify calls too:
|
|
currentUserID := "53afcf05-38a3-43c3-82af-8bbbe0e4a149"
|
|
userID := refresh.Claims.Subject
|
|
if userID != currentUserID {
|
|
ctx.StopWithStatus(iris.StatusUnauthorized)
|
|
return
|
|
}
|
|
*/
|
|
|
|
// All OK, re-generate the new pair and send to client.
|
|
generateTokenPair(ctx, j)
|
|
}
|