How should I implement 'Token Based Authentication' to a set of web APIs in a secure way using PHP and MySQL (without using OAuth)?

被刻印的时光 ゝ 提交于 2019-11-29 07:26:14

Based on our discussion, you could do something akin to OAuth2.0. I would recommend implementing the full spec, but since it is your application, you could make changes.

Here is a graph from RFC 6750

+--------+                               +---------------+
 |        |--(A)- Authorization Request ->|   Resource    |
 |        |                               |     Owner     |
 |        |<-(B)-- Authorization Grant ---|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(C)-- Authorization Grant -->| Authorization |
 | Client |                               |     Server    |
 |        |<-(D)----- Access Token -------|  (Slim API)   |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(E)----- Access Token ------>|    Resource   |
 |        |                               |     Server    |
 |        |<-(F)--- Protected Resource ---|               |
 +--------+                               +---------------+

In slim, you could have as few as three endpoints:

POST username/password:

/oauth/v1/authenticate/

returns { token: foo }

GET where {token} is your unique token

/oauth/v1/token/{token}

returns { username: joe, permissions['page:admin','users:full'], expires: 123456}

DELETE pass the {token}

/oauth/v1/token/revoke

replies with 200 OK and an empty body.

Now, how it works:

  • When they auth, just return a JSON object with your token, which the client stores in something like a cookie.
  • The client passes this to your resource server in a header as identified in the RFC section 2.1 (which looks up the user permissions in a database/nosql/whatever):

GET /resource HTTP/1.1

 Host: server.example.com

 Authorization: Bearer mF_9.B5f-4.1JqM

Your resource server contacts the Slim API on the back-end to determine your permissions. The server then decides what you're allowed to see.

If you don't like sending it as a header, see section 2.2, which describes how to send it in the body, or section 2.3 which sends it as a URI query.

These obviously do not need to be different servers. You can implement it how you wish.

You could do it this way.

Each user has a private key (a random alpha-numeric x long string) - which is unique to each user.

On every request, you can hash_hmac the request with their private key which will generate a unique request key for each user. For example:

 Request:
 GET /v1/products/coffee

 Private key: 
 ww9k6fcysu30sbuzu7ez57z2kzvefyxwosrjcnwo

I would then generate a request key like;

hash_hmac("sha1", "GET /v1/products/coffee", "ww9k6fcysu30sbuzu7ez57z2kzvefyxwosrjcnwo");

This would give me a request key of: 45751dce6ef93655a71e7b82a6179591c346c2c1 for this GET request only. It will also ensure the client intended for this endpoint and hasn't been tampered with.

On the receiving end you would perform the same hash_hmac routine with the users private key (they would need to pass in their username in the request - for example - to perform a lookup to fetch the private key) and compare the two results.

hash_hmac("sha1", $_SERVER['REQUEST_METHOD'] . " " . $_SERVER['REDIRECT_URL'], $user_private_key);

For added bonus, you would get a hash for POST/PUT body content and append that in the request query string and authenticate that on the receiving end. For example;

$bodyhash = md5(implode(",", $_POST));

When a user logs out, deactivate the private key and assign them a new one on next login.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!