Вход для аутентификации Файлы cookie не устанавливаются в браузере - API-интерфейс Flask + интерфейс Angular 7 - PullRequest
0 голосов
/ 04 мая 2019

Я создаю веб-приложение с помощью API-интерфейса Flask с угловым интерфейсом. API работает на http://localhost:5000, интерфейс на http://localhost:4200.

По какой-то причине куки не устанавливаются в браузере. Заголовки браузера кажутся правильными, но при проверке chrome devtools -> Application -> Storage -> Cookies я не вижу никаких файлов cookie.

Что мне здесь не хватает?

Соответствующие части Python:

from octowc.web_app.resources import (
    Test, Refresh, Login,
    Logout
)
from flask import (
    Flask, render_template
)
from flask_jwt_extended import JWTManager
from flask_sqlalchemy import SQLAlchemy, sqlalchemy
from octowc.config import Config
from flask_cors import CORS
from flask_restful import Api


app = Flask(__name__)
api = Api(app)
jwt = JWTManager(app)
CORS(app, origins=['http://localhost:4200']

# JWT token configuration
app.config['JWT_SECRET_KEY'] = conf.jwt_secret_key
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
app.config['JWT_ACCESS_COOKIE_PATH'] = '/api/'
app.config['JWT_REFRESH_COOKIE_PATH'] = '/token'
app.config['JWT_COOKIE_CSRF_PROTECT'] = True
app.config['JWT_COOKIE-DOMAIN'] = 'localhost'
app.config['JWT_COOKIE_SECURE'] = False
app.config['CSRF_IN_COOKIES'] = False

api.add_resource(Refresh, '/token/refresh')
api.add_resource(Test, '/api/test')
api.add_resource(Login, '/login')
api.add_resource(Logout, '/logout')


class Refresh(Resource):
    @jwt_refresh_token_required
    def post(self):
        user = get_jwt_identity()
        refresh_token = create_access_token(identity=user)
        resp = jsonify({'refresh': True})
        set_access_cookies(resp, refresh_token)
        return resp


class Test(Resource):
    @jwt_required
    def get(self):
        resp = {'Test': 'Successfull!'}
        return resp

    @jwt_required
    def post(self):
        resp = {'Test': 'Successfull!'}
        return resp


class Logout(Resource):
    def post(self):
        resp = jsonify({'Logout:': True})
        unset_jwt_cookies(resp)
        return resp


class Login(Resource):

    parser = reqparse.RequestParser()
    parser.add_argument(
        'username',
        type=str,
        required=True
    )
    parser.add_argument(
        'password',
        type=str,
        required=True
    )

    def post(self):
        try:
            data = Login.parser.parse_args()
        except BadRequest as _:
            return {'Error': 'Bad request'}, 400

        username = data['username']
        password = data['password']
        if username != 'test' or password != 'test':
            return {'Error': 'Credentials incorrect'}, 401

        access_token = create_access_token(identity=username)
        refresh_token = create_refresh_token(identity=username)
        resp = jsonify({'Login': True})
        set_access_cookies(resp, access_token)
        set_refresh_cookies(resp, refresh_token)
        return resp

Соответствующие угловые части:

auth.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { CookieService } from 'ngx-cookie-service';
import { Router } from '@angular/router';
import { environment } from 'src/environments/environment';
import { JwtHelperService } from '@auth0/angular-jwt';

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json',
    withCredentials: 'true'
  })
};

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  baseUrl = environment.apiUrl;
  jwtHelper = new JwtHelperService();
  decodedToken: any;

  constructor(
    private http: HttpClient,
    private cookieService: CookieService,
    private router: Router) { }

  login(model: any) {
    return this.http.post(this.baseUrl + 'login', model, httpOptions);
      .pipe(
        map((response: any) => {
          console.log(response);
  }))};

  logOut() {
    this.cookieService.deleteAll('/', 'localhost');
  }

  isLoggedIn() {
    const cookie = this.cookieService.get('access_token');
    return !!cookie;
  }

  getCSRFToken(type: string) {
    return this.cookieService.get(`csrf_${type}_token`);
  }

  getCookie(type: string) {
    return this.cookieService.get(`${type}_token`);
  }
}

Заголовки браузера

Request URL: http://localhost:5000/login
Request Method: POST
Status Code: 200 OK
Remote Address: 127.0.0.1:5000
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Origin: http://localhost:4200
Content-Length: 20
Content-Type: application/json
Date: Sat, 04 May 2019 08:09:48 GMT
Server: Werkzeug/0.15.2 Python/3.7.3
Set-Cookie: access_token_cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NTY5NTczODgsIm5iZiI6MTU1Njk1NzM4OCwianRpIjoiMTRhZGFkNjEtMTdjYi00NGMwLTg4NjctNGJiOTZiZmY0OGY2IiwiZXhwIjoxNTU2OTU4Mjg4LCJpZGVudGl0eSI6InRlc3QiLCJmcmVzaCI6ZmFsc2UsInR5cGUiOiJhY2Nlc3MiLCJjc3JmIjoiODFlNjYxZmYtZTcxNy00ZmY0LTkxNGItN2EzZWZjZTE5MmM2In0.TV7K2RXDMQsmmmrsmaFiLv6CwmB-Wg5DIdkWfv3IEMI; HttpOnly; Path=/api/
Set-Cookie: csrf_access_token=81e661ff-e717-4ff4-914b-7a3efce192c6; Path=/
Set-Cookie: refresh_token_cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NTY5NTczODgsIm5iZiI6MTU1Njk1NzM4OCwianRpIjoiOGVhNWVjNWItMDg5Ny00ZTA2LWIxYWItYjkxNGVhZjI4MjE2IiwiZXhwIjoxNTU5NTQ5Mzg4LCJpZGVudGl0eSI6InRlc3QiLCJ0eXBlIjoicmVmcmVzaCIsImNzcmYiOiJlYzYyMDBkYi1lNmJkLTQ4MDktOTM5Yi01YWZjMWZiZjBlZjAifQ.1kt9N4DTBGH27eBDiz8fiLAiiWPNiEN9q2ddpgCkMw0; HttpOnly; Path=/token
Set-Cookie: csrf_refresh_token=ec6200db-e6bd-4809-939b-5afc1fbf0ef0; Path=/
Vary: Origin
POST /login HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Content-Length: 37
Accept: application/json, text/plain, */*
Origin: http://localhost:4200
withCredentials: true
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 OPR/60.0.3255.59
Content-Type: application/json
Referer: http://localhost:4200/login?returnUrl=%2Fhome
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
{username: "test", password: "test"}
password: "test"
username: "test"

1 Ответ

0 голосов
/ 05 мая 2019

Я думаю, что интерфейс и сервер должны обслуживаться с одного хоста. и порт для файлов cookie для работы. Я думаю, что есть плагин webpack прокси справиться с этим. - vimalloc 22 часа назад webpack.js.org/configuration/dev-server/#devserverproxy - vimalloc 22 часов назад

Спасибо, что указали мне в правильном направлении! Настройка прокси сделала свое дело. Оказывается, вы также можете настроить его с помощью Angular CLI https://juristr.com/blog/2016/11/configure-proxy-api-angular-cli/

Спасибо!

...