Warm tip: This article is reproduced from serverfault.com, please click

Why CORS Error Access-Control-Allow-Credentials' header in the response is ''?

发布于 2020-11-30 08:01:44

I make @vue/cli 4.5.9 / axios app with data reading from Laravel 7 Backend REST(with "tymon/jwt-auth": "^1.0", "barryvdh/laravel-cors": "^1.0.5") API using axios and I got error :

has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.

But here I check that all Credentials are filled : https://prnt.sc/vsngs5 I have on client side:

        console.log('+login settingCredentialsConfig::')
        console.log(settingCredentialsConfig)

        axios.post(apiUrl + '/login', userCredentials, settingCredentialsConfig)
            .then((response) => {
                if (typeof response.data.access_token === 'undefined' || typeof response.data.user === 'undefined') {
                    commit('auth_error') // call auth_error mutation to make changes to vuex store
                    bus.$emit('authLoggedError')
                    return
                }

I import settingCredentialsConfig from setings file defined as :

export const settingCredentialsConfig = {
    withCredentials:true,
    headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Credentials':true
    }
}

and in src/main.js axios defined :

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from 'axios'

export const bus = new Vue()

axios.defaults.crossDomain = true
let apiUrl = process.env.VUE_APP_API_URL
Vue.config.productionTip = false
Vue.prototype.$http = axios

Vue.config.productionTip = false

import Vue2Filters from 'vue2-filters'
Vue.use(Vue2Filters)

new Vue({
  router,
  store,
  bus,
  render: h => h(App)
}).$mount('#app')

Vue.use({
  install (Vue) {
    Vue.prototype.$api = axios.create({
      baseURL: apiUrl
    })
  }
})

In other vue page I have request to backend using the same settingCredentialsConfig var and it works ok. But it does not refere auth of backend User model. On backenbd part I have app/User.php :

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject; // JWT SUPPORT

class User extends Authenticatable implements JWTSubject  // JWT SUPPORT
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function getJWTIdentifier()  // JWT SUPPORT
    {
        return $this->getKey();
    }
    public function getJWTCustomClaims()  // JWT SUPPORT
    {
        return [];
    }


}

In app/Http/Controllers/API/AuthController.php :

<?php

namespace App\Http\Controllers\API;

use Auth;
use Config;
use DB;
use Validator;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;

//use Illuminate\Support\Str;

class AuthController extends Controller
{

    protected $maxAttempts = 1; // Default is 5
    protected $decayMinutes = 1; // Default is 1

    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('jwt.auth', ['except' => ['login', 'register', 'activate']]);
    }

    public function login(Request $request)
    {
        $credentials = $request->only('email', 'password');

        \Log::info('LOGIN +1 $credentials ::');


        if ($token = $this->guard('api')->attempt($credentials /*,['exp' => \Carbon\Carbon::now()->addHours(4)->timestamp]*/)) {
            $loggedUser = $this->guard('api')->user();

            \Log::info('LOGIN +2 $loggedUser ::');

            return $this->respondWithToken($token);
        }

        \Log::info('LOGIN -3 ::');

        return response()->json(['error' => 'Unauthorized'], 401);
    }

    public function getAuthenticatedUser()
    {
        try {
            \Log::info('-1 getAuthenticatedUser ::');
            if ( ! $user = JWTAuth::parseToken()->authenticate()) {
                return response()->json(['user_not_found'], 404);
            }


        } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
            \Log::info('-2 getAuthenticatedUser ::');

            return response()->json(['token_expired'], $e->getStatusCode());


        } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
            \Log::info('-3 getAuthenticatedUser ::');

            return response()->json(['token_invalid'], $e->getStatusCode());


        } catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
            \Log::info('-4 getAuthenticatedUser ::');

            return response()->json(['token_absent'], $e->getStatusCode());
        }

        // the token is valid and we have found the user via the sub claim
        return response()->json(compact('user'));
    }

    public function me()
    {
        return response()->json($this->guard('api')->user());
    }

    public function logout()
    {
        \Log::info('-1 logout ::' . print_r(-1, true));

        $this->guard('api')->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    public function refresh()
    {
        return $this->respondWithToken($this->guard()->refresh());
    }

    protected function respondWithToken($token)
    {
        $loggedUser = $this->guard()->user();

        return response()->json([
            'access_token' => $token,
            'user'         => $loggedUser,
            'token_type'   => 'bearer',
            'expires_in'   => $this->guard('api')->factory()->getTTL() * 999360 // TOFIX
        ]);
    }

    public function guard()
    {
        return \Auth::Guard('api');
    }
}

and in config/cors.php :

<?php

return [

    'paths' => ['api/*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => false,

];

I generated file config/jwt.php with command :

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

and left it unchanged . In file app/Http/Kernel.php :

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,

        'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',
        'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
...

What did I miss ?

Thanks!

Questioner
mstdmstd
Viewed
0
tamrat 2020-11-30 16:15:52

Change the supports_credentials value in your config/cors.php file to true.