2

i'm trying to create an app using Angular2 as the frontend and cakephp 3 as the REST Api, the authentication works fine, but when i try to acces any other url i get the 401 Unauthorized status, and i noticed that the Request Method is OPTIONS instead of GET that i used in my code, and the Authorization header with my token is not sent to the server:

enter image description here

Here's my user.service.ts code:

constructor(private http: Http,
          private router: Router,
) { }

login(email: string, password: string){
    let headers: Headers = new Headers({ 'Accept': 'application/json','Content-Type': 'application/json' });
    let options: RequestOptions = new RequestOptions({headers: headers});

    return this.http.post('http://www.students.com/api/users/token.json', {email: email, password: password}, options)
  .map((data: Response)=> data.json())
  .subscribe(
    (data)=> this.handlData(data),
    (error)=> this.handlError(error)
  );
}

getSessionData(){
    let token = localStorage.getItem('usr_token');
    let headers = new Headers({ 'Accept': 'application/json', 'Authorization': 'Bearer ' + token });
    let options: RequestOptions = new RequestOptions({headers: headers});

    return this.http.get('http://www.students.com/api/users/getSessionData', options).subscribe(
       data => console.log(data),
       err => console.log(err)
     );
}

handlData(data){

    if(data.success){
        let usrData = data.data.user;
        this.user = new User(usrData.email, usrData.firstname, usrData.lastname, usrData.role, data.data.token);
        localStorage.setItem('id_token', data.data.token);
    }
}

handlError(error){
   console.log(error);
}

i tried to use angular2-jwt module but i had the same error, and to make sure that my API works fine, i tested it with Postman chrome extension and it worked as expected:

enter image description here

and here's my Apache2 VirtualHost configuration

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/students
    ServerName www.students.com
    <Directory /var/www/html/students>                        
        Require all granted  
        Options Indexes FollowSymLinks Includes
        AllowOverride all
    </Directory>
    Header always set Access-Control-Allow-Origin "*"                   
    Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"
    Header always set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"
</VirtualHost>

any one had the same problem? any ideas why is that happening?

1 Answers1

3

This is not an issue with Angular but with your backend. Angular is trying to make a preflight request by checking if the server returns OK on a OPTIONS request. You should set-up your backend to respond with 200 or 204 for OPTIONS requests.

If you are using node.js:

app.use('/api', (req, res, next) => {
    /** Browser check for pre-flight request to determine whether the server is webdav compatible */
    if ('OPTIONS' == req.method) {
        res.sendStatus(204);
    }
    else next();
});

Or laravel (PHP):

App::before(function($request)
{
    // Sent by the browser since request come in as cross-site AJAX
    // The cross-site headers are sent via .htaccess
    if ($request->getMethod() == "OPTIONS")
        return new SuccessResponse();
});

This will tell the browser that the server can handle webdav request methods appropriately.

UPDATE: added by asker on CakePHP:

public function initialize() {
    parent::initialize();

    if($this->request->is('options')) {
        $this->response->statusCode(204);
        $this->response->send();
        die();
    }

    $this->Auth->allow(['add', 'token']);
}
borislemke
  • 8,446
  • 5
  • 41
  • 54
  • 1
    Thank you very much, now i understand why this is happening, so in case someone had the same issue in Cakephp3 this is how i solved the problem: `public function initialize() { parent::initialize(); if($this->request->is('options')) { $this->response->statusCode(200); $this->response->send(); die(); } $this->Auth->allow(['add', 'token']); }` , but i still don't understand why an OPTIONS request is sent before every POST or GET request – Yassine El Khanboubi Dec 13 '16 at 12:35
  • @yassineelkhanboubi POST, GET, PUT and similar methods (I think they're correctly called WebDav methods) were not mainstream on servers. So the browser needs to send a "preflight" OPTIONS request to the server to determine whether the server really support POST and GET(and PUT, PATCH etc.) methods. In my experience, I had to do this on some server environments but not on others. So I think there is more appropriate way to do this on the server level. I updated my answer to include your solution with Cake PHP 3. – borislemke Dec 13 '16 at 15:46
  • @yassineelkhanboubi note that 204 is a more appropriate response code for empty content. – borislemke Dec 13 '16 at 15:48
  • thank you very much for your explanation and update. – Yassine El Khanboubi Dec 13 '16 at 16:53