Why does the browser see requests made from one localhost subdomain to another on the same domain as `sec-fetch-site:cross-site`? - fetch-metadata

I have a dockerised local dev env for an SPA. The setup is that there are front-end and back-end subdomains on localhost, running over https.
I.e.:
fe.localhost
be.localhost
(These are the actual domains I'm using; they are not placeholders nor obfuscated examples.)
According to the MDN docs (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site#directives) -
same-site
The request initiator and the server hosting the resource have the same scheme, domain and/or subdomain, but not necessarily the same port.
Why then are requests from the front-end to the back-end being reported by the browser as cross-site?
This happens when e.g. my front-end (which is browsable on https://fe.localhost makes a GET or POST request to the back-end on https://be.localhost/api/people/login.
Here's the full server code:
<?php
include('debugTools.php');
global $data;
$data = [];
function loggedOut() {
header('Content-Type: application/json; charset=utf-8');
return [
'login' => [
'valid' => false,
'message' => 'Not currently logged in.'
]
];
}
function login() {
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type');
session_start();
return [
'login' => [
'valid' => true,
]
];
}
function loggedIn() {
header('Content-Type: application/json; charset=utf-8');
return [
'login' => [
'valid' => true,
]
];
}
$path = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];
switch($method) {
case 'POST':
switch ($path) {
case '/api/people/login':
$data = login();
break;
}
break;
default: // GET
switch ($path) {
case '/api/people/login':
if(session_id()) {
$data = loggedIn();
} else {
$data = loggedOut();
}
break;
}
break;
}
echo json_encode($data);

Related

Cakephp authentication results invalid only on stateless verification

Using the authentication plugin, I'm unable to verify my credentials via json. I can use Form, Jwt and Basic(For testing it works). The error returned is 'FAILURE_CREDENTIALS_INVALID'. Here's the sample code "simplified for brevity"
public function token()
{
$result = $this->Authentication->getResult();
if ($result->isValid()) {
//new jwt token etc
$message = true;
$this->log($result->getStatus());
}else{
$message = false;
$this->log($result->getStatus());
}
$this->set([
'message' => $message,
]);
$this->viewBuilder()->setOption('serialize', ['message']);
}
My application class has the method
public function getAuthenticationService(ServerRequestInterface $request) : AuthenticationServiceInterface
{
$service = new AuthenticationService();
$service->setConfig([
'unauthenticatedRedirect' => '/users/login',
'queryParam' => 'redirect',
]);
$fields = [
'username' => 'email',
'password' => 'password'
];
// Load the authenticators, you want session first
$service->loadAuthenticator('Authentication.Session');
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => '/users/login'
]);
//Testing jwt
$service->loadAuthenticator('Authentication.Jwt', [
'secretKey' => Security::getSalt()]);
// Load identifiers
$service->loadIdentifier('Authentication.Password', compact('fields'));
$service->loadIdentifier('Authentication.JwtSubject');
return $service;
}
The app is serving some json well if I pass in the jwt, but somehow I can't figure out how to request a new one, when the old expires.
Here's my middlewareQueue:
//some code
$middlewareQueue
->add(new ErrorHandlerMiddleware(Configure::read('Error')))
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime'),
]))
->add(new RoutingMiddleware($this))
->add($csrf)
->add(new BodyParserMiddleware())
->add(new AuthenticationMiddleware($this));
//more code
I DO NOT use basic
As mentioned in the comments, if you want to authenticate using the form authenticator on your token retrieval endpoint, then you need to make sure that you include the URL path of that endpoint in the authenticators loginUrl option.
That option accepts an array, so it should be as simple as this:
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => [
'/users/login',
'/api/users/token.json',
],
]);
The error that you were receiving was because the form authenticator simply wasn't applied on the token endpoint, and therefore the authentication service would go to the next authenticator, the JWT authenticator, which isn't bound to a specific endpoint, and therefore can run for all endpoints, and the JWT authenticator of course expects a different payload, it looks for a token, and if it can't find it, it will return the error status FAILURE_CREDENTIALS_INVALID.

Error trying to test laravel/passport auth cicle

I'm issuing some problems with Laravel/Passport while trying to test the auth flow of my application...
The stranger thing is that it only happens when I'm on testing env. The expects the 200 http code but instead receive a 500.
I'm using the Laravel 5.8 version, I'm also have installed Horizon and Telescope. My PHP version is 7.3.4.
My test
public function testLogin()
{
$user = factory(\App\Models\User::class)->create();
$response = $this->json('post', 'v1/login', [
'email' => $user->email,
'password' => 'secret',
]);
$response->assertStatus(Response::HTTP_OK);
}
My AuthController
public function login(AuthRequest $request)
{
try {
$credentials = $request->only(['email', 'password']);
throw_if(!auth()->attempt($credentials), UnauthorizedAccess::class, __('auth.auth_required'));
$accessToken = $this->handleAccessToken($credentials);
return response()->json($accessToken);
} catch (UnauthorizedAccess $ex) {
return response()->json([
'success' => false,
'message' => $ex->getMessage()
], Response::HTTP_UNAUTHORIZED);
}
}
private function handleAccessToken(array $credentials)
{
$user = User::where(['email' => $credentials['email']])->first();
$http = new Client();
$response = $http->post(url('oauth/token'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => $user->client->id,
'client_secret' => $user->client->secret,
'username' => $credentials['email'],
'password' => $credentials['password'],
'scope' => '*',
],
]);
return json_decode((string) $response->getBody(), true);
}
This is the erro that is produced:
on terminal
1) Tests\Feature\AuthTest::testLogin
Expected status code 200 but received 500.
Failed asserting that false is true.
laravel.log
[2019-04-26 09:02:53] local.ERROR: Client authentication failed {"exception":"[object] (League\\OAuth2\\Server\\Exception\\OAuthServerException(code: 4): Client authentication failed at /home/***/workspace/***/vendor/league/oauth2-server/src/Exception/OAuthServerException.php:138)
[stacktrace]
[2019-04-26 09:02:53] testing.ERROR: Client error: `POST http://***/oauth/token` resulted in a `401 Unauthorized` response:
{"error":"invalid_client","error_description":"Client authentication failed","message":"Client authentication failed"}
{"userId":"52b19d0d-e0c5-4924-84ae-d07700c672df","exception":"[object] (GuzzleHttp\\Exception\\ClientException(code: 401): Client error: `POST http://***/oauth/token` resulted in a `401 Unauthorized` response:
{\"error\":\"invalid_client\",\"error_description\":\"Client authentication failed\",\"message\":\"Client authentication failed\"}
at /home/***/workspace/***/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:113)
[stacktrace]
Well, the error seems to be something with Guzzle itself while in testing env. That happens because Guzzle is set for the default env.
Then, I solve it following the answer gave by #lawrencedawson, that you can see here

Basic authentication in Lumen

I have already written an API call for check authentication using Laravel. I need to move that controller to Lumen for use it as micro service.
This is my controller in Laravel.
public function byCredantial(Request $request)
{
$user = [
'email' => $request->input('email'),
'password' => $request->input('password')
];
if (Auth::attempt($user)) {
$response = $this->getSuccess(Auth::user()->id);
return response()->json($response, 200);
} else {
$response = $this->getError($user);
return response()->json($response, 401);
}
}
Lumen doc is not provide how to do such authentication. They has not function for check creadential is correct. How can i do this in Lumen. Is this possible?
You can do this in Lumen. Facades are disabled by default (if you want to enable it you can see the instructions in the documentation), but I would not recommend enabling the facades as the add additional overhead to your application. Instead, I would modify your function to call app('auth'). This will return the class that the Auth facade proxies without loading all the other facades.
public function byCredantial(Request $request)
{
$user = [
'email' => $request->input('email'),
'password' => $request->input('password')
];
$auth = app('auth');
if ($auth->attempt($user)) {
$response = $this->getSuccess($auth->user()->id);
return response()->json($response, 200);
} else {
$response = $this->getError($user);
return response()->json($response, 401);
}
}
Also, I would recommend reading the authentication documentation and placing the bulk of this code in the AuthServiceProvider.

Laravel 5 API not allowing access to new Angular2 app

I have a Laravel API which is now allowing a new Angular2 application I am building to have access to itself. I get the following error:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3073' is therefore not allowed access. The response had HTTP status code 404.
This is confusing me quite a bit as I already have two applications (one also an Angular2) application that is communicating with the API fine with no issues whatsoever. I have also created CORS middleware in the API which provides the appropriate headers to allows these applications through.
CORS Middleware
<?php
namespace App\Http\Middleware;
use Closure;
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
class CORS {
public function handle($request, Closure $next) {
header("Access-Control-Allow-Origin: *");
$headers = [
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Headers'=> 'Content-Type, X-Auth-Token, Origin'
];
if($request->getMethod() == "OPTIONS") {
// The client-side application can set only headers allowed in Access-Control-Allow-Headers
return Response::make('OK', 200, $headers);
}
$response = $next($request);
foreach($headers as $key => $value) {
$response->header($key, $value);
}
return $response;
}
}
My Angular2 application is attempting to call my API using this code:
import { Injectable } from "#angular/core";
import { Http, Headers, RequestOptions } from "#angular/http";
import { Observable } from "rxjs";
#Injectable()
export class AuthService {
constructor(private _http: Http) {
}
login(loginDetails):Observable {
let body = JSON.stringify(loginDetails);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this._http.post('http://#####-brain.dev/api/v1', body, options)
.map(res => res.json());
}
}
Does anyone have any advice as to why this particular application is not receiving the Access-Control-Allow-Origin header header? I have put a breakpoint within my CORS middleware and it doesn't hit it at all which is bizarre as it is calling exactly the same endpoint that my other application is and that is working fine.
I have also noticed that it is only POST requests that it doesn't allow through.
Edit: Here is my app/Http/Kernel.php file in the Laravel API:
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'cors' => 'App\Http\Middleware\CORS',
'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class
];
Thanks!

Yii2 oauth2 client_credential token authentication

I use Filsh/yii2-oauth2-server,
I can use client token using $_pos to access my API.
In controller:
if (!isset($_post['token'])){
//exeption
}
else{
token = $_post('token');
}
if ((Accesstokens::isAccessTokenValid($token))) {
// do some thing.
}
in my Accesstokens model :
public static function isAccessTokenValid($token)
{
if (empty($token)) {
return false;
}
$query = (new \yii\db\Query())
->select(['access_token','expires'])
->from('oauth_access_tokens')
->where(['access_token' => $token])
// ->limit(10)
->one();
if (empty($query)) {
return false;
}
$expire = $query['expires'];
return $expire > date('Y-m-d h:i:s');
}
If I use password (user_credential) token, I can implementation bearer auth using :
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
'authenticator' => [
'class' => CompositeAuth::className(),
'authMethods' => [
['class' => HttpBearerAuth::className()],
['class' => QueryParamAuth::className(), 'tokenParam' => 'accessToken'],
]
],
'exceptionFilter' => [
'class' => ErrorToExceptionFilter::className()
],
]);
}
But this method is using user table in authenticate user, not oauth_client table to authenticate client without user.
How to authenticate token base on client table only, without user table?
Request:
GET http://myapi.com/api/www/index.php/oauth2/token
grand_type = client_credentials
client_id = id1
client_secret = secret1
Response:
"access_token": "thisistheccesstoken",
"expires_in": 31104000,
"token_type": "Bearer",
"scope": "default"
Request:
GET http://myapi.com/api/www/index.php/oauth2/v1/get/frogs
HEADER Authorization: Bearer thisistheaccesstoken
Response:
all the frogs
How to use: HEADER Authorization: Bearer? Can anyone help?