Sessions in token based authentication

前端 未结 8 1103
轻奢々
轻奢々 2020-12-28 15:54

I am building an app in PHP Lumen which returns a token upon login. I am not sure how to proceed beyond this.

How am I supposed to maintain a session using these to

相关标签:
8条回答
  • 2020-12-28 16:21

    Let's assume you want to build an APP with.

    1. ReactJS
    2. REST API with PHP
    3. Using JWT

    1. Introduction

    You must forget about sessions when building REST API's.

    REST API's are meant to be stateless, so they must not depend on sessions, they must process requests with only the data given by the client.

    2. Authentication

    All the client wants to do is only to exchange some username & password for a token.

    This is an example HTTP request

    POST /api/v1/authentication HTTP/1.1
    Host: localhost
    Content-Type: application/json
    {
        "username": "foo",
        "password": "bar"
    }
    

    And the response is:

    {
        "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
    

    3. let's go into more details into request/response

    How our API will process the authentication request?

    1. It will check if a user with username foo & password bar is founded and it's active in DB

    2. It will generate a JWT (Json Web Token)

    3. It will return response containing the JWT

    This is some super simple auth method, just for example.

    public function authAction()
    {
      /** Get your payload somehow */
      $request = $_POST;
    
      //Validate if username & password are given/
    
      $user = $this->model->auth($username, $password);
    
      if(!$user) {
        //throw error for not valid credentials
      }
    
      $jwt = $this->jwt->create($user);
    
      //return response with $jwt
    }
    

    As you see they are no sessions set or anything.

    How our client side will process the response?

    The client could use some package like superagent for handling the requests & responses to our API this way the process will be simplified to this:

      let data = {
        username: email,
        password: password
      };
    
      request
        .post('/api/v1/authentication')
        .set('Content-Type', 'application/json')
        .send(data)
        .end(function (error, response) {
          //response.body.token
        });
    

    4. Creating JWT on server side

    You could use some 3RD PT package for generating and validating JWT instead of writing it yourself.

    Look at this package, you can see how it's done.

    And remember to always create strong signatures. I recommend using RSA keys

    I am not advertising or supporting this project, just found it useful to share it here. I had never used it, I'm using something similar to this on my NodeJS projects.

    5. Saving JWT on client side

    They are two ways as you already know localStorage & cookies For me I am using cookies, because:

    1. They are a bit more secure.
    2. Expire date can be set without implementing some custom logic.
    3. Older browser support (very old browsers, so it's not that important).

    But it's all up to you.

    6. Using JWT

    From now on every request to server you must include your JWT.

    In your REST API you must write a method to validate the JWT and exchanging it for user object.

    Example request:

      let jwt = ...; //GET IT FROM LOCALSTORAGE OR COOKIE
    
      request
        .get('/api/v1/posts')
        .set('Content-Type', 'application/json')
        .set('Authorization', jwt)
        .end(function (error, response) {
    
        });
    

    How API will process this request

    public function postsAction()
    {
      $jwt = $this->headers->get('Authorization');
    
      if(!$this->jwt->validate($jwt)) {
        //throw unauthorized error
      }
    
      $user = $this->model->exchangeJWT($jwt);
    
      //Your logic here
    }
    

    7. Expire date & cookie

    If you are using cookie to save your JWT, be careful with setting the expire dates.

    The cookie expire date must be equal to the JWT expire date.

    0 讨论(0)
  • 2020-12-28 16:34

    You can store it in the browser's localStorage, then set it in the header for each request to the server.

    0 讨论(0)
  • 2020-12-28 16:36

    I'll write down a quick todo and best practices, as there are many ways to do it with code.

    Backend

    • (POST) login route {email, password} it will create a token. You can use JWT (Json Web Token) The token will be returned to the client. Inside the token, you can store some basic details: user id, username, token expiration, user type etc. https://jwt.io/

    Client

    • login request, pass {email, password}.

      On success, get the token and store it locally, localstorage is preferred, but cookie is possible as well.

    • on each page load with your react app, you should have a function check for that token, it will decrypt it, and get the details for further use.

      I mean get the username, user id etc. More important if you will want to add it, is the "expiration", if the token was expired you redirect the user to login page, OR you can re-request for a new token, it really depends on your app.

    • logout, is quite simple... simply remove the token from the client side and redirect to login page.

    • Make sure that for "authenticated" pages, you check that the token exists, and even further you can check the user type.

    ** for client side decoding of JWT, you can use: https://www.npmjs.com/package/jwt-client

    0 讨论(0)
  • 2020-12-28 16:38

    What I usually do is to keep the token in the local storage, this way I can persist the token even if the user leaves the site.

    localStorage.setItem('app-token', theTokenFromServer);
    

    Every time the user loads the page, the first thing I do is to look for the existence of the token.

    token = localStorage.getItem('app-token');
    

    If using react, I'd keep the token on the global state (using redux for example):

    function loadAppToken(token) {
      return {
        type: 'LOAD_TOKEN',
        payload: { token },
      };
    }
    

    With vanilla javascript I'd keep it on my connection utility. Which might look something like the following:

    const token = localStorage.getItem('app-token');
    
    export function request(config) {
       const { url, ...others } = config;
    
       return fetch(url, {
         ...others,
         credentials: 'include',
         headers: {
           'Authorization': `Bearer ${token}`
         },
       });
    }
    

    I'd still have a fetch utility in a react app, similar to the previous code, but I'd send the token in the options, by getting it in a redux middleware for every single request.

    0 讨论(0)
  • 2020-12-28 16:38

    Currently working on same type of application using lumen for API. Following 3 steps for Token based Authentication in Lumen with JWT:

    1. Create Token and return after login success

    public function login(Request $request) {
        $token = $this->jwt->attempt(['user_name' => $data['user_name'], 'password' => $data['password']]); //$token = $this->jwt->attempt($data); 
        if (!$token) {
            $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_INVALID_USER, 'error' => array(Messages::MSG_INVALID_USER)));
            return response()->json($response);
        } else {
            $user = \Auth::setToken($token)->user();
            $data = array('token' => $token,'user_id' => $user->id);
            $response = array('success' => true, 'data' => $data, 'detail' => array('message' => Messages::MSG_SUCCESS, 'error' => null));
            return response()->json($response);
        }
    }
    

    2. Define middleware for token verification

    public function handle($request, Closure $next, $guard = null) {
        try {
            $token = $request->header('X-TOKEN');
            $user_id = $request->header('X-USER');
            $user = \Auth::setToken($token)->user();
            if ($user && $user->id == $user_id) {
                return $next($request);
            } else {
                $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERR_INVALID_TOKEN, 'error' => Messages::MSG_ERR_INVALID_TOKEN));
                return response()->json($response);
            }
        } catch (Exception $ex) {
            $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERROR_500, 'error' => array($ex)));
            return response()->json($response);
        }
    }
    

    3. Store token in localstorage or in cookies

    localStorage.setItem("Token", JSON.stringify(TokenData));
    TokenData = JSON.parse(localStorage.getItem("Token"));
    

    or

    $.cookie('Token', JSON.stringify(TokenData), {expires: 1, path: '/'});
    TokenData = JSON.parse($.cookie("Token"));
    

    4. Send token with every request in headers

    Request with custom headers

    $.ajax({
        url: 'foo/bar',
        headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
    });
    

    Headers to every request

    $.ajaxSetup({
            headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
        });
    

    Hope it'll help.

    Note: Add some checks and data validations while reading data from localstorage or cookies .

    0 讨论(0)
  • 2020-12-28 16:40

    You actually don't need any ReactJS or VanillaJS. Just pure HTML and PHP actually. What I do is just store it as a cookie.

    First of all, as you receive the token from Lumen, save it in your user database for specific user. Then set user id and accesstoken as cookies which expire after a certain time with this code:

    setcookie('userid',$userid, time()+(3600 * 24 * 15),"/");
    setcookie('accesstoken',$accesstoken, time()+(3600 * 24 * 15),"/");
    header('Location: /home.php');
    //You can change the 15 in setcookie() to amount of days the cookie will expire in.
    //The "/" in setcookie is important, because it ensures the cookies will be available on every page the user visits on your website.
    //The header function redirects to your home page after log in
    

    Then below is how your home page would look. It checks if accesstoken cookie exists, if it does, it double checks that the token matches the current token in the user database. If it's a match, it shows 'logged in' page. If not, you should show/redirect to login page.

    <?php
    if (isset($_COOKIE['accesstoken']))
    {
    //connect to your user database and check that the cookie accesstoken matches
    // if it doesn't match, deal with it appropriately, such as deleting all cookies then redirecting to login page.
    }
    ?>
    <!DOCTYPE HTML>
    <html>
    <head>
    <title>Sup</title>
    </head>
    <body>
    <?php if (isset($_COOKIE['accesstoken'])){ ?>
    
    <h1>User logged in!</h1>
    <h3>Do whatever you need to do if user is logged in</h3>
    
    <?php } else { ?>
    
    <h1>No accesstoken found</h1>
    <h3>More than likely you will want to show login page here</h3>
    
    <?php } ?>
    </body>
    </html>
    

    and then to logout is simple. The code below removes accesstokens by setting them to expired:

    setcookie("accesstoken", "", time() - 3600);
    setcookie("userid", "", time() - 3600);
    header('Location: /youareloggedout.html');
    

    Remember, that is the BASICS of a functional log in / log out system. If I explained all security measures needed, this post would be even longer. Be sure to do your research. Some topics to start you off are prepared statements and preventing XSS attacks. :)

    0 讨论(0)
提交回复
热议问题