【问题标题】:auth0 jwt authentication Error parsing token: Token used before issuedauth0 jwt authentication 解析令牌时出错:发出之前使用的令牌
【发布时间】:2020-06-14 06:36:50
【问题描述】:

我已成功使用 Auth0 设置身份验证,但是当 Auth0 重定向回我的回调时,我无法访问 ajax 端点。

但是,如果我刷新页面(无需再次通过身份验证),我现在可以了。我可以在 localStorage 中看到 access_token 已设置。因此,当我刷新令牌时设置但第一次没有设置,这似乎很奇怪。

我怀疑我在设置 localStorage 之前加载了组件,这意味着对 api 的请求失败。但是我不确定修复方法。

我收到错误 Error parsing token: Token used before issued 和 401 对 api 的请求

我的代码如下:

我正在使用库

"github.com/auth0/go-jwt-middleware"
"github.com/dgrijalva/jwt-go"

我这里初始化token的验证:

func (h *Page) Init() error {
    h.JWTMiddleware = jwtmiddleware.New(jwtmiddleware.Options{
        ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
            aud := os.Getenv("AUTH0_API_AUDIENCE")
            checkAudience := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
            if !checkAudience {
                return token, errors.New("Invalid audience.")
            }
            // verify iss claim
            iss := os.Getenv("AUTH0_DOMAIN")
            fmt.Println("iss == ", iss)
            checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false)
            if !checkIss {
                return token, errors.New("Invalid issuer.")
            }

            cert, err := getPemCert(token)
            if err != nil {
                fmt.Println("could not get cert: %+v", err)
                return cert, err
            }

            result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
            return result, nil
        },
        SigningMethod: jwt.SigningMethodRS256,
    })

    return nil
}

我在这里配置pem和我的中间件

func (h *Page) authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // Get the client secret key
        err := h.JWTMiddleware.CheckJWT(c.Writer, c.Request)
        if err != nil {
            // Token not found
            fmt.Println("error checking jwt ", err)           <<<---- FAILS HERE!
            c.Abort()
            c.Writer.WriteHeader(http.StatusUnauthorized)
            c.Writer.Write([]byte("Unauthorized"))
            return
        }
    }
}

func getPemCert(token *jwt.Token) (string, error) {
    cert := ""
    resp, err := http.Get(os.Getenv("AUTH0_DOMAIN") + ".well-known/jwks.json")
    if err != nil {
        return cert, err
    }
    defer resp.Body.Close()

    var jwks = Jwks{}
    err = json.NewDecoder(resp.Body).Decode(&jwks)

    if err != nil {
        return cert, err
    }

    x5c := jwks.Keys[0].X5c
    for k, v := range x5c {
        if token.Header["kid"] == jwks.Keys[k].Kid {
            cert = "-----BEGIN CERTIFICATE-----\n" + v + "\n-----END CERTIFICATE-----"
        }
    }
    if cert == "" {
        return cert, errors.New("unable to find appropriate key")
    }

    return cert, nil
}

我的反应前端就在这里


const AUTH0_CLIENT_ID = "xxx7vOQeStuKmaNfwxoWX"//'{{ $variables := index .Globals "jokish.so"}}{{index $variables "auth0-key"}}';
const AUTH0_DOMAIN = "xxx.eu.auth0.com"//'{{ $variables := index .Globals "jokish.so"}}{{index $variables "domain"}}';
const AUTH0_CALLBACK_URL = "http://localhost:9000/jokes/callback"//'{{ $variables := index .Globals "jokish.so"}}{{index $variables "callback"}}';
const AUTH0_API_AUDIENCE = "https://xxx.eu.auth0.com/api/v2/"//'{{ $variables := index .Globals "jokish.so"}}{{index $variables "audience"}}';

class App extends React.Component {
    parseHash() {
        this.auth0 = new auth0.WebAuth({
            domain: AUTH0_DOMAIN,
            clientID: AUTH0_CLIENT_ID
        });
        this.auth0.parseHash(window.location.hash, (err, authResult) => {
            if (err) {
                return console.log(err);
            }
            if (
                authResult !== null &&
                authResult.accessToken !== null &&
                authResult.idToken !== null
            ) {
                localStorage.setItem("access_token", authResult.accessToken);
                localStorage.setItem("id_token", authResult.idToken);
                localStorage.setItem(
                    "profile",
                    JSON.stringify(authResult.idTokenPayload)
                );
                window.location = window.location.href.substr(
                    0,
                    window.location.href.indexOf("#")
                );
            }
        });
    }

    setup() {
        $.ajaxSetup({
            beforeSend: (r) => {
                console.log("setting ajax up with token ", localStorage.getItem("access_token"))
                if (localStorage.getItem("access_token")) {
                    r.setRequestHeader(
                        "Authorization",
                        "Bearer " + localStorage.getItem("access_token")
                    );
                }
            }
        });
    }

    setState() {
        let idToken = localStorage.getItem("id_token");
        if (idToken) {
            this.loggedIn = true;
        } else {
            this.loggedIn = false;
        }
    }

    componentWillMount() {
        this.setup();
        this.parseHash();
        this.setState();
    }

    render() {
        if (this.loggedIn) {
            return <LoggedIn />;
        }
        return <Home />;
    }
}

class Home extends React.Component {
    constructor(props) {
        super(props);
        this.authenticate = this.authenticate.bind(this);
    }
    authenticate() {
        this.WebAuth = new auth0.WebAuth({
            domain: AUTH0_DOMAIN,
            clientID: AUTH0_CLIENT_ID,
            scope: "openid profile",
            audience: AUTH0_API_AUDIENCE,
            responseType: "token id_token",
            redirectUri: AUTH0_CALLBACK_URL
        });
        this.WebAuth.authorize();
    }

    render() {
        return (
            <div className="container">
                <div className="row">
                    <div className="col-xs-8 col-xs-offset-2 jumbotron text-center">
                        <h1>Jokeish</h1>
                        <p>A load of Dad jokes XD</p>
                        <p>Sign in to get access </p>
                        <a
                            onClick={this.authenticate}
                            className="btn btn-primary btn-lg btn-login btn-block"
                        >
                            Sign In
                        </a>
                    </div>
                </div>
            </div>
        );
    }
}

class LoggedIn extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            jokes: []
        };

        this.serverRequest = this.serverRequest.bind(this);
        this.logout = this.logout.bind(this);
    }

    logout() {
        localStorage.removeItem("id_token");
        localStorage.removeItem("access_token");
        localStorage.removeItem("profile");
        location.reload();
    }

    serverRequest() {
        $.get("http://localhost:9000/jokes/api/jokes", res => {
            this.setState({
                jokes: res
            });
        });
    }

    componentDidMount() {
        this.serverRequest();
    }

    render() {
        return (
            <div className="container">
                <br />
                <span className="pull-right">
          <a onClick={this.logout}>Log out</a>
        </span>
                <h2>Jokeish</h2>
                <p>Let's feed you with some funny Jokes!!!</p>
                <div className="row">
                    <div className="container">
                        {this.state.jokes.map(function(joke, i) {
                            return <Joke key={i} joke={joke} />;
                        })}
                    </div>
                </div>
            </div>
        );
    }
}

class Joke extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            liked: "",
            jokes: []
        };
        this.like = this.like.bind(this);
        this.serverRequest = this.serverRequest.bind(this);
    }

    like() {
        let joke = this.props.joke;
        this.serverRequest(joke);
    }
    serverRequest(joke) {
        $.post(
            "http://localhost:9000/jokes/api/jokes/like/" + joke.id,
            { like: 1 },
            res => {
                console.log("res... ", res);
                this.setState({ liked: "Liked!", jokes: res });
                this.props.jokes = res;
            }
        );
    }

    render() {
        return (
            <div className="col-xs-4">
                <div className="panel panel-default">
                    <div className="panel-heading">
                        #{this.props.joke.id}{" "}
                        <span className="pull-right">{this.state.liked}</span>
                    </div>
                    <div className="panel-body joke-hld">{this.props.joke.joke}</div>
                    <div className="panel-footer">
                        {this.props.joke.likes} Likes &nbsp;
                        <a onClick={this.like} className="btn btn-default">
                            <span className="glyphicon glyphicon-thumbs-up" />
                        </a>
                    </div>
                </div>
            </div>
        );
    }
}
ReactDOM.render(<App />, document.getElementById("app"));

【问题讨论】:

    标签: reactjs go jwt auth0 http-status-code-401


    【解决方案1】:

    我在使用相同 JWT 库的 auth0 设置中也遇到了这个问题。

    这是一个已知问题,因为 github.com/dgrijalva/jwt-go 检查了 iat (Issued At) JWT 字段并要求它严格小于 time.Now()

    问题是,在现实世界中,服务器的时钟可能会漂移(即使在运行 NTP 的情况下,Windows 尤其容易出现这种情况),这可能会导致检查失败。

    如果/当 jwt-go 的 v4 发布时,它将不再进行 iat 检查(根据最新的 JWT 标准),但在此之前,解决方案是在检查中添加一些余地。这可以通过jwt.Claims 的自定义实现来完成:

    const issuedAtLeewaySecs = 1 
    
    type MyClaims struct {
        jwt.StandardClaims
    }
    
    func (c *MyClaims) Valid() bool {
        c.StandardClaims.IssuedAt -= issuedAtLeewaySecs
        valid := c.StandardClaims.Valid()
        c.StandardClaims.IssuedAt += issuedAtLeewaySecs
        return valid
    }
    

    现在您需要使用 jwt.ParseWithClaims(token, &amp;MyClaims{}, ...) 函数而不是 jwt.Parse 来使用您的声明实现。

    很遗憾,go-jwt-middleware 没有为您提供该选项,而是在内部隐藏了此调用。除了要求他们发布修复此问题的更新之外,唯一要做的就是提供他们的模块并在these lines 中进行相关更改。

    编辑:刚刚看到 go-jwt-middleware 将很快从 jwt-go 迁移出来,因为它没有维护,这有望解决这些问题。

    【讨论】:

      猜你喜欢
      • 2017-10-21
      • 2018-10-29
      • 2017-12-29
      • 2014-11-08
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 2017-08-15
      • 2019-06-03
      相关资源
      最近更新 更多