I'm making an authorization system in PHP, and I came across this Bearer scheme of passing JWT tokens, I read [RFC 6750][1]. I've got the following doubts:
How is this improving the security?
The server responses the client with a JWT token in its body after a successful authorization and login, and now when the client makes another request, I am not clear how to actually do that, I want to send token from client in Authorization header in the request, so now should I just prefix "Bearer" to the token which I received in the previous response from the server and If yes, then server on receiving the Authorization header, should just split the string with space, and take the second value from the obtained array and then decode it? For example Authorization: Bearer fdbghfbfgbjhg_something, how is server supposed to handle this, decodeFunc(explode(" ", $this->getRequest()->getHeader("Authorization"))[1])?
[1]: https://www.rfc-editor.org/rfc/rfc6750
1.Improving the security because if token is not sent in the header that sent in url, it will be logged by the network system, the server log ....
2.A good function to get Bearer tokens
/**
* Get header Authorization
* */
function getAuthorizationHeader(){
$headers = null;
if (isset($_SERVER['Authorization'])) {
$headers = trim($_SERVER["Authorization"]);
}
else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
$headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
} elseif (function_exists('apache_request_headers')) {
$requestHeaders = apache_request_headers();
// Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
$requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
//print_r($requestHeaders);
if (isset($requestHeaders['Authorization'])) {
$headers = trim($requestHeaders['Authorization']);
}
}
return $headers;
}
/**
* get access token from header
* */
function getBearerToken() {
$headers = getAuthorizationHeader();
// HEADER: Get the access token from the header
if (!empty($headers)) {
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
return $matches[1];
}
}
return null;
}
I would recommend to use the following RegEx to check, if it's a valid jwt-token:
/Bearer\s((.*)\.(.*)\.(.*))/
and access it also with matches[1].
This is the structure of a JWT-Token, see: https://jwt.io/
Related
So I'm following the default setup as in the Sanctum documentation, i make a request o csrf cookie route so Laravel triggers the Cookies and set to the request the XSRF-TOKEN, but the documentation says:
Laravel stores the current CSRF token in an encrypted XSRF-TOKEN cookie that is included with each response generated by the framework. You can use the cookie value to set the X-XSRF-TOKEN request header.
https://laravel.com/docs/9.x/csrf#csrf-x-csrf-token
This is the code of frontend i retrieve the Cookie every time and send in the X-XSRF-TOKEN header
const authLink = setContext(async (_, { headers }) => {
const authToken = localStorage.getItem(AUTH_TOKEN_NAME)
console.log(Cookie.get('XSRF-TOKEN'))
return {
headers: {
...headers,
authorization: authToken ? `Bearer ${authToken}` : '',
'X-XSRF-TOKEN': Cookie.get('XSRF-TOKEN')
}
}
})
Of course, this is executed everytime a request is made, so here is the relevant console log:
As you can see, for some reason Laravel is sending randomly a decrypted cookie in plain text, and some times a encrypted one.
Of course, it's important that only the encrypted cookie should be returned because the header X-XSRF-TOKEN is decrypted on backend every time, as you can see in \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken:
/**
* Get the CSRF token from the request.
*
* #param \Illuminate\Http\Request $request
* #return string
*/
protected function getTokenFromRequest($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
try {
$token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
} catch (DecryptException $e) {
$token = '';
}
}
return $token;
}
So, does anyone has any idea why Laravel is returning to me a randomly encrypted and decrypted csrf token, instead of only the encrypted one? Or even weirder, it's returning both tokens, in each request one type?
I have a specific API call that simply times out when using Content-Type = "application/json; charset=UTF-8".
If I remove this header (I'm just trying this via the Google Postman client), I immediately receive a response.
I'm using Laravel PHP to return a response.
I feel like I may be missing something extremely obvious, such as some config setting not allowing this content-type, but I'm not sure which direction to be looking.
Code:
Route file:
Route::post('getUserStats', 'UsersController#getUserStats');
Response function:
public function getUserStats(Request $request) {
return Response::JSON(array('foo'=>'bar'));
$input = #file_get_contents('php://input');
$input = json_decode($input);
$userEmail = $input->customer->email;
$user = User::where('email', $userEmail)->first();
if (!$user) {
return 'no user match for email: ' . $userEmail;
}
return $user->buildStatsHTML();
}
Notice I am simply trying to return anything to start, I've also simply tried to return a string to no avail.
The request being sent is simply localhost:8000/api/tokenAuth/getUserStats with the header "Content-Type: application/json; charset=UTF-8".
I'm new in Slim Framework 3. I have a problem to accessing web service that has a Api Key header value. I have had a Api Key value and wanna access the web service to get JSON data. Here is my slim get method code:
$app->get('/getbooking/{id}', function (Request $request, Response $response, $args) {
$id = $args['id'];
$string = file_get_contents('http://maindomain.com/webapi/user/'.$id);
//Still confuse how to set header value to access the web service with Api Key in the header included.
});
I have tried the web service in Postman (chrome app) to access and I get the result. I use GET method and set Headers value for Api Key.
But how to set Headers value in Slim 3 to get access the web service?
Thanks for advance :)
This doesn't actually have anything to do with Slim. There are multiple ways to make an HTTP request from within PHP including streams (file_get_contents()), curl and libraries such as Guzzle.
Your example uses file_get_contents(), so to set a header there, you need to create a context. Something like this:
$app->get('/getbooking/{id}', function (Request $request, Response $response, $args) {
$id = $args['id']; // validate $id here before using it!
// add headers. Each one is separated by "\r\n"
$options['http']['header'] = 'Authorization: Bearer {token here}';
$options['http']['header'] .= "\r\nAccept: application/json";
// create context
$context = stream_context_create($options);
// make API request
$string = file_get_contents('http://maindomain.com/webapi/user/'.$id, 0, $context);
if (false === $string) {
throw new \Exception('Unable to connect');
}
// get the status code
$status = null;
if (preg_match('#HTTP/[0-9\.]+\s+([0-9]+)#', $http_response_header[0], $matches)) {
$status = (int)$matches[1];
}
// check status code and process $string here
}
I am using the league\oauth2-server and have it working perfectly when I am using the GET http verb.
However when doing a PUT request I am getting hit with
Call to undefined method League\OAuth2\Server\Util\Request::PUT()
The specific code that is throwing this error is a function Server/Resource.php file
$accessToken = $this->getRequest()->{$method}($this->tokenKey);
From the function
protected function determineAccessToken($headersOnly = false)
{
if ($header = $this->getRequest()->header('Authorization')) {
// Check for special case, because cURL sometimes does an
// internal second request and doubles the authorization header,
// which always resulted in an error.
//
// 1st request: Authorization: Bearer XXX
// 2nd request: Authorization: Bearer XXX, Bearer XXX
if (strpos($header, ',') !== false) {
$headerPart = explode(',', $header);
$accessToken = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $headerPart[0]));
} else {
$accessToken = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header));
}
$accessToken = ($accessToken === 'Bearer') ? '' : $accessToken;
} elseif ($headersOnly === false) {
$method = $this->getRequest()->server('REQUEST_METHOD');
$accessToken = $this->getRequest()->{$method}($this->tokenKey);
}
if (empty($accessToken)) {
throw new Exception\InvalidAccessTokenException('Access token is missing');
}
return $accessToken;
}
I am using the POSTMAN request client to test the requests
Its throwing that error because PUT requests for the web service are not allowed. And that makes sense because you really should never need to do a PUT request for an OAuth2 request. PUT tells the RESTful service on the other end you are trying to update a specific entity. OAuth2 has no entities to update, only to retrieve.
Perhaps a better understanding of what you are trying to do might explain why you are using a PUT, but for OAuth2 it should always be a GET request.
if you set the 'http_headers_only' setting to true and send the token in the correct header, it should work.
By correct HTTP header I mean "Authorization: Bearer " instead of access_token: that can be seen in your postman screenshot. Also please note that "Authorization" is the key and the value consists of both "Bearer" keyword and your token, separated by a space character.
I have this code from Yii Example
private function _checkAuth()
{
// Check if we have the USERNAME and PASSWORD HTTP headers set?
if(!(isset($_SERVER['HTTP_X_USERNAME']) and isset($_SERVER['HTTP_X_PASSWORD']))) {
// Error: Unauthorized
$this->_sendResponse(401);
}
$username = $_SERVER['HTTP_X_USERNAME'];
$password = $_SERVER['HTTP_X_PASSWORD'];
// Find the user
$user=User::model()->find('LOWER(username)=?',array(strtolower($username)));
$this->_sendResponse('200','$username');
if($user===null) {
// Error: Unauthorized
$this->_sendResponse(401, 'Error: User Name is invalid');
}
else if(!$user->validatePassword($password)) {
// Error: Unauthorized
$this->_sendResponse(401, 'Error: User Password is invalid');
}
}
How to set header information for authentication.
How to set HTTP_X_USERNAME and HTTP_X_PASSWORD in request;
for the name, value and body of RESTClient addon?
Thank for advance
Hi I have resolved this issue with following server side code, set the HTTP Request headers as you are already doing on client side.
private function _checkAuth()
{
// Check if we have the USERNAME and PASSWORD HTTP headers set?
$headers = apache_request_headers();
if(!(isset($headers['X_'.self::APPLICATION_ID.'_USERNAME']) and isset($headers['X_'.self::APPLICATION_ID.'_PASSWORD']))) {
// Error: Unauthorized
$this->_sendResponse(401);
}
$username = $headers['X_'.self::APPLICATION_ID.'_USERNAME'];
$password = $headers['X_'.self::APPLICATION_ID.'_PASSWORD'];
}
I understand your question (same as this post in yii forum) relates to the RESTClient addon. To set your headers in the RESTClient addon: use the 'headers' functionality:
List item
menu 'Headers'
'Custom Header'
'Name' : X_PASSWORD
'Value' : emo
And the same with X_USERNAME.
HTH,
JM.