fix: reworked csrf
This commit is contained in:
parent
475266eef6
commit
bd868bb724
16 changed files with 39 additions and 150 deletions
|
|
@ -150,8 +150,6 @@ paths:
|
||||||
description: User not found
|
description: User not found
|
||||||
'500':
|
'500':
|
||||||
description: Unknown server error
|
description: Unknown server error
|
||||||
security:
|
|
||||||
- JwtAuthCookies: []
|
|
||||||
patch:
|
patch:
|
||||||
operationId: updateUser
|
operationId: updateUser
|
||||||
summary: Partially update a user account
|
summary: Partially update a user account
|
||||||
|
|
@ -160,7 +158,6 @@ paths:
|
||||||
Password updates must be done via the dedicated auth-service (`/auth/`).
|
Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||||
Fields not provided in the request body remain unchanged.
|
Fields not provided in the request body remain unchanged.
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
|
||||||
- name: user_id
|
- name: user_id
|
||||||
in: path
|
in: path
|
||||||
description: User ID (primary key)
|
description: User ID (primary key)
|
||||||
|
|
@ -229,7 +226,7 @@ paths:
|
||||||
'500':
|
'500':
|
||||||
description: Unknown server error
|
description: Unknown server error
|
||||||
security:
|
security:
|
||||||
- JwtAuthCookies: []
|
XsrfAuthHeader: []
|
||||||
'/users/{user_id}/titles':
|
'/users/{user_id}/titles':
|
||||||
get:
|
get:
|
||||||
operationId: getUserTitles
|
operationId: getUserTitles
|
||||||
|
|
@ -405,14 +402,11 @@ paths:
|
||||||
description: User or title not found
|
description: User or title not found
|
||||||
'500':
|
'500':
|
||||||
description: Unknown server error
|
description: Unknown server error
|
||||||
security:
|
|
||||||
- JwtAuthCookies: []
|
|
||||||
patch:
|
patch:
|
||||||
operationId: updateUserTitle
|
operationId: updateUserTitle
|
||||||
summary: Update a usertitle
|
summary: Update a usertitle
|
||||||
description: User updating title list of watched
|
description: User updating title list of watched
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
|
||||||
- name: user_id
|
- name: user_id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
|
|
@ -455,13 +449,12 @@ paths:
|
||||||
'500':
|
'500':
|
||||||
description: Internal server error
|
description: Internal server error
|
||||||
security:
|
security:
|
||||||
- JwtAuthCookies: []
|
- XsrfAuthHeader: []
|
||||||
delete:
|
delete:
|
||||||
operationId: deleteUserTitle
|
operationId: deleteUserTitle
|
||||||
summary: Delete a usertitle
|
summary: Delete a usertitle
|
||||||
description: User deleting title from list of watched
|
description: User deleting title from list of watched
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
|
||||||
- name: user_id
|
- name: user_id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
|
|
@ -486,42 +479,9 @@ paths:
|
||||||
'500':
|
'500':
|
||||||
description: Internal server error
|
description: Internal server error
|
||||||
security:
|
security:
|
||||||
- JwtAuthCookies: []
|
- XsrfAuthHeader: []
|
||||||
components:
|
components:
|
||||||
parameters:
|
parameters:
|
||||||
accessToken:
|
|
||||||
name: access_token
|
|
||||||
in: cookie
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: jwt
|
|
||||||
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.x.y
|
|
||||||
description: |
|
|
||||||
JWT access token.
|
|
||||||
csrfToken:
|
|
||||||
name: xsrf_token
|
|
||||||
in: cookie
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
pattern: '^[a-zA-Z0-9_-]{32,64}$'
|
|
||||||
example: abc123def456ghi789jkl012mno345pqr
|
|
||||||
description: |
|
|
||||||
Anti-CSRF token (Double Submit Cookie pattern).
|
|
||||||
Stored in non-HttpOnly cookie, readable by JavaScript.
|
|
||||||
Must be echoed in `X-XSRF-TOKEN` header for state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
csrfTokenHeader:
|
|
||||||
name: X-XSRF-TOKEN
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
pattern: '^[a-zA-Z0-9_-]{32,64}$'
|
|
||||||
description: |
|
|
||||||
Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
|
||||||
Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
example: abc123def456ghi789jkl012mno345pqr
|
|
||||||
cursor:
|
cursor:
|
||||||
in: query
|
in: query
|
||||||
name: cursor
|
name: cursor
|
||||||
|
|
@ -780,3 +740,11 @@ components:
|
||||||
Review:
|
Review:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
|
securitySchemes:
|
||||||
|
XsrfAuthHeader:
|
||||||
|
type: apiKey
|
||||||
|
in: header
|
||||||
|
name: X-XSRF-TOKEN
|
||||||
|
description: |
|
||||||
|
Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||||
|
Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||||
|
|
|
||||||
|
|
@ -23,3 +23,5 @@ components:
|
||||||
$ref: "./parameters/_index.yaml"
|
$ref: "./parameters/_index.yaml"
|
||||||
schemas:
|
schemas:
|
||||||
$ref: "./schemas/_index.yaml"
|
$ref: "./schemas/_index.yaml"
|
||||||
|
securitySchemes:
|
||||||
|
$ref: "./securitySchemes/_index.yaml"
|
||||||
|
|
@ -2,9 +2,3 @@ cursor:
|
||||||
$ref: "./cursor.yaml"
|
$ref: "./cursor.yaml"
|
||||||
title_sort:
|
title_sort:
|
||||||
$ref: "./title_sort.yaml"
|
$ref: "./title_sort.yaml"
|
||||||
accessToken:
|
|
||||||
$ref: "./access_token.yaml"
|
|
||||||
csrfToken:
|
|
||||||
$ref: "./xsrf_token_cookie.yaml"
|
|
||||||
csrfTokenHeader:
|
|
||||||
$ref: "./xsrf_token_header.yaml"
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
name: access_token
|
|
||||||
in: cookie
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: jwt
|
|
||||||
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.x.y"
|
|
||||||
description: |
|
|
||||||
JWT access token.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
name: xsrf_token
|
|
||||||
in: cookie
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
pattern: "^[a-zA-Z0-9_-]{32,64}$"
|
|
||||||
example: "abc123def456ghi789jkl012mno345pqr"
|
|
||||||
description: |
|
|
||||||
Anti-CSRF token (Double Submit Cookie pattern).
|
|
||||||
Stored in non-HttpOnly cookie, readable by JavaScript.
|
|
||||||
Must be echoed in `X-XSRF-TOKEN` header for state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
name: X-XSRF-TOKEN
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
pattern: "^[a-zA-Z0-9_-]{32,64}$"
|
|
||||||
description: |
|
|
||||||
Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
|
||||||
Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
example: "abc123def456ghi789jkl012mno345pqr"
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
get:
|
get:
|
||||||
summary: Get user title
|
summary: Get user title
|
||||||
operationId: getUserTitle
|
operationId: getUserTitle
|
||||||
security:
|
|
||||||
- JwtAuthCookies: []
|
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: user_id
|
name: user_id
|
||||||
|
|
@ -37,9 +35,8 @@ patch:
|
||||||
description: User updating title list of watched
|
description: User updating title list of watched
|
||||||
operationId: updateUserTitle
|
operationId: updateUserTitle
|
||||||
security:
|
security:
|
||||||
- JwtAuthCookies: []
|
- XsrfAuthHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
|
||||||
- in: path
|
- in: path
|
||||||
name: user_id
|
name: user_id
|
||||||
required: true
|
required: true
|
||||||
|
|
@ -87,9 +84,8 @@ delete:
|
||||||
description: User deleting title from list of watched
|
description: User deleting title from list of watched
|
||||||
operationId: deleteUserTitle
|
operationId: deleteUserTitle
|
||||||
security:
|
security:
|
||||||
- JwtAuthCookies: []
|
- XsrfAuthHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
|
||||||
- in: path
|
- in: path
|
||||||
name: user_id
|
name: user_id
|
||||||
required: true
|
required: true
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
get:
|
get:
|
||||||
summary: Get user info
|
summary: Get user info
|
||||||
operationId: getUsersId
|
operationId: getUsersId
|
||||||
security:
|
|
||||||
- JwtAuthCookies: []
|
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: user_id
|
name: user_id
|
||||||
|
|
@ -30,15 +28,15 @@ get:
|
||||||
|
|
||||||
patch:
|
patch:
|
||||||
summary: Partially update a user account
|
summary: Partially update a user account
|
||||||
security:
|
|
||||||
- JwtAuthCookies: []
|
|
||||||
description: |
|
description: |
|
||||||
Update selected user profile fields (excluding password).
|
Update selected user profile fields (excluding password).
|
||||||
Password updates must be done via the dedicated auth-service (`/auth/`).
|
Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||||
Fields not provided in the request body remain unchanged.
|
Fields not provided in the request body remain unchanged.
|
||||||
operationId: updateUser
|
operationId: updateUser
|
||||||
|
security:
|
||||||
|
XsrfAuthHeader: []
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
# - $ref: '../parameters/xsrf_token_header.yaml'
|
||||||
- name: user_id
|
- name: user_id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
|
|
|
||||||
11
api/securitySchemes/_index.yaml
Normal file
11
api/securitySchemes/_index.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# accessToken:
|
||||||
|
# $ref: "./access_token.yaml"
|
||||||
|
# csrfToken:
|
||||||
|
# $ref: "./xsrf_token_cookie.yaml"
|
||||||
|
XsrfAuthHeader:
|
||||||
|
type: apiKey
|
||||||
|
in: header
|
||||||
|
name: X-XSRF-TOKEN
|
||||||
|
description: |
|
||||||
|
Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||||
|
Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||||
|
|
@ -6,6 +6,10 @@ import TitlePage from "./pages/TitlePage/TitlePage";
|
||||||
import { LoginPage } from "./pages/LoginPage/LoginPage";
|
import { LoginPage } from "./pages/LoginPage/LoginPage";
|
||||||
import { Header } from "./components/Header/Header";
|
import { Header } from "./components/Header/Header";
|
||||||
|
|
||||||
|
import { OpenAPI } from "./api";
|
||||||
|
|
||||||
|
OpenAPI.WITH_CREDENTIALS = true
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
const username = localStorage.getItem("username") || undefined;
|
const username = localStorage.getItem("username") || undefined;
|
||||||
const userId = localStorage.getItem("userId");
|
const userId = localStorage.getItem("userId");
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
|
||||||
export { OpenAPI } from './core/OpenAPI';
|
export { OpenAPI } from './core/OpenAPI';
|
||||||
export type { OpenAPIConfig } from './core/OpenAPI';
|
export type { OpenAPIConfig } from './core/OpenAPI';
|
||||||
|
|
||||||
export type { accessToken } from './models/accessToken';
|
|
||||||
export type { csrfToken } from './models/csrfToken';
|
|
||||||
export type { csrfTokenHeader } from './models/csrfTokenHeader';
|
|
||||||
export type { cursor } from './models/cursor';
|
export type { cursor } from './models/cursor';
|
||||||
export type { CursorObj } from './models/CursorObj';
|
export type { CursorObj } from './models/CursorObj';
|
||||||
export type { Image } from './models/Image';
|
export type { Image } from './models/Image';
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
/* generated using openapi-typescript-codegen -- do not edit */
|
|
||||||
/* istanbul ignore file */
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* JWT access token.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type accessToken = string;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
/* generated using openapi-typescript-codegen -- do not edit */
|
|
||||||
/* istanbul ignore file */
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Anti-CSRF token (Double Submit Cookie pattern).
|
|
||||||
* Stored in non-HttpOnly cookie, readable by JavaScript.
|
|
||||||
* Must be echoed in `X-XSRF-TOKEN` header for state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type csrfToken = string;
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
/* generated using openapi-typescript-codegen -- do not edit */
|
|
||||||
/* istanbul ignore file */
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
|
||||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type csrfTokenHeader = string;
|
|
||||||
|
|
@ -135,16 +135,12 @@ export class DefaultService {
|
||||||
* Password updates must be done via the dedicated auth-service (`/auth/`).
|
* Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||||
* Fields not provided in the request body remain unchanged.
|
* Fields not provided in the request body remain unchanged.
|
||||||
*
|
*
|
||||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
|
||||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
*
|
|
||||||
* @param userId User ID (primary key)
|
* @param userId User ID (primary key)
|
||||||
* @param requestBody
|
* @param requestBody
|
||||||
* @returns User User updated successfully. Returns updated user representation (excluding sensitive fields).
|
* @returns User User updated successfully. Returns updated user representation (excluding sensitive fields).
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static updateUser(
|
public static updateUser(
|
||||||
xXsrfToken: string,
|
|
||||||
userId: number,
|
userId: number,
|
||||||
requestBody: {
|
requestBody: {
|
||||||
/**
|
/**
|
||||||
|
|
@ -175,9 +171,6 @@ export class DefaultService {
|
||||||
path: {
|
path: {
|
||||||
'user_id': userId,
|
'user_id': userId,
|
||||||
},
|
},
|
||||||
headers: {
|
|
||||||
'X-XSRF-TOKEN': xXsrfToken,
|
|
||||||
},
|
|
||||||
body: requestBody,
|
body: requestBody,
|
||||||
mediaType: 'application/json',
|
mediaType: 'application/json',
|
||||||
errors: {
|
errors: {
|
||||||
|
|
@ -316,9 +309,6 @@ export class DefaultService {
|
||||||
/**
|
/**
|
||||||
* Update a usertitle
|
* Update a usertitle
|
||||||
* User updating title list of watched
|
* User updating title list of watched
|
||||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
|
||||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
*
|
|
||||||
* @param userId
|
* @param userId
|
||||||
* @param titleId
|
* @param titleId
|
||||||
* @param requestBody
|
* @param requestBody
|
||||||
|
|
@ -326,7 +316,6 @@ export class DefaultService {
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static updateUserTitle(
|
public static updateUserTitle(
|
||||||
xXsrfToken: string,
|
|
||||||
userId: number,
|
userId: number,
|
||||||
titleId: number,
|
titleId: number,
|
||||||
requestBody: {
|
requestBody: {
|
||||||
|
|
@ -341,9 +330,6 @@ export class DefaultService {
|
||||||
'user_id': userId,
|
'user_id': userId,
|
||||||
'title_id': titleId,
|
'title_id': titleId,
|
||||||
},
|
},
|
||||||
headers: {
|
|
||||||
'X-XSRF-TOKEN': xXsrfToken,
|
|
||||||
},
|
|
||||||
body: requestBody,
|
body: requestBody,
|
||||||
mediaType: 'application/json',
|
mediaType: 'application/json',
|
||||||
errors: {
|
errors: {
|
||||||
|
|
@ -358,16 +344,12 @@ export class DefaultService {
|
||||||
/**
|
/**
|
||||||
* Delete a usertitle
|
* Delete a usertitle
|
||||||
* User deleting title from list of watched
|
* User deleting title from list of watched
|
||||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
|
||||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
|
||||||
*
|
|
||||||
* @param userId
|
* @param userId
|
||||||
* @param titleId
|
* @param titleId
|
||||||
* @returns any Title successfully deleted
|
* @returns any Title successfully deleted
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static deleteUserTitle(
|
public static deleteUserTitle(
|
||||||
xXsrfToken: string,
|
|
||||||
userId: number,
|
userId: number,
|
||||||
titleId: number,
|
titleId: number,
|
||||||
): CancelablePromise<any> {
|
): CancelablePromise<any> {
|
||||||
|
|
@ -378,9 +360,6 @@ export class DefaultService {
|
||||||
'user_id': userId,
|
'user_id': userId,
|
||||||
'title_id': titleId,
|
'title_id': titleId,
|
||||||
},
|
},
|
||||||
headers: {
|
|
||||||
'X-XSRF-TOKEN': xXsrfToken,
|
|
||||||
},
|
|
||||||
errors: {
|
errors: {
|
||||||
401: `Unauthorized — missing or invalid auth token`,
|
401: `Unauthorized — missing or invalid auth token`,
|
||||||
403: `Forbidden — user not allowed to delete title`,
|
403: `Forbidden — user not allowed to delete title`,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { DefaultService } from "../../api";
|
import { DefaultService } from "../../api";
|
||||||
import type { UserTitleStatus } from "../../api";
|
import type { UserTitleStatus } from "../../api";
|
||||||
import { useCookies } from 'react-cookie';
|
// import { useCookies } from 'react-cookie';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ClockIcon,
|
ClockIcon,
|
||||||
|
|
@ -19,8 +19,8 @@ const STATUS_BUTTONS: { status: UserTitleStatus; icon: React.ReactNode; label: s
|
||||||
];
|
];
|
||||||
|
|
||||||
export function TitleStatusControls({ titleId }: { titleId: number }) {
|
export function TitleStatusControls({ titleId }: { titleId: number }) {
|
||||||
const [cookies] = useCookies(['xsrf_token']);
|
// const [cookies] = useCookies(['xsrf_token']);
|
||||||
const xsrfToken = cookies['xsrf_token'] || null;
|
// const xsrfToken = cookies['xsrf_token'] || null;
|
||||||
|
|
||||||
const [currentStatus, setCurrentStatus] = useState<UserTitleStatus | null>(null);
|
const [currentStatus, setCurrentStatus] = useState<UserTitleStatus | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
@ -46,7 +46,7 @@ export function TitleStatusControls({ titleId }: { titleId: number }) {
|
||||||
try {
|
try {
|
||||||
// 1) Если кликнули на текущий статус — DELETE
|
// 1) Если кликнули на текущий статус — DELETE
|
||||||
if (currentStatus === status) {
|
if (currentStatus === status) {
|
||||||
await DefaultService.deleteUserTitle(xsrfToken, userId, titleId);
|
await DefaultService.deleteUserTitle(userId, titleId);
|
||||||
setCurrentStatus(null);
|
setCurrentStatus(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ export function TitleStatusControls({ titleId }: { titleId: number }) {
|
||||||
setCurrentStatus(added.status);
|
setCurrentStatus(added.status);
|
||||||
} else {
|
} else {
|
||||||
// уже есть запись — PATCH
|
// уже есть запись — PATCH
|
||||||
const updated = await DefaultService.updateUserTitle(xsrfToken, userId, titleId, { status });
|
const updated = await DefaultService.updateUserTitle(userId, titleId, { status });
|
||||||
setCurrentStatus(updated.status);
|
setCurrentStatus(updated.status);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue