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
|
||||
'500':
|
||||
description: Unknown server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
patch:
|
||||
operationId: updateUser
|
||||
summary: Partially update a user account
|
||||
|
|
@ -160,7 +158,6 @@ paths:
|
|||
Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||
Fields not provided in the request body remain unchanged.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
||||
- name: user_id
|
||||
in: path
|
||||
description: User ID (primary key)
|
||||
|
|
@ -229,7 +226,7 @@ paths:
|
|||
'500':
|
||||
description: Unknown server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
XsrfAuthHeader: []
|
||||
'/users/{user_id}/titles':
|
||||
get:
|
||||
operationId: getUserTitles
|
||||
|
|
@ -405,14 +402,11 @@ paths:
|
|||
description: User or title not found
|
||||
'500':
|
||||
description: Unknown server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
patch:
|
||||
operationId: updateUserTitle
|
||||
summary: Update a usertitle
|
||||
description: User updating title list of watched
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
||||
- name: user_id
|
||||
in: path
|
||||
required: true
|
||||
|
|
@ -455,13 +449,12 @@ paths:
|
|||
'500':
|
||||
description: Internal server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
- XsrfAuthHeader: []
|
||||
delete:
|
||||
operationId: deleteUserTitle
|
||||
summary: Delete a usertitle
|
||||
description: User deleting title from list of watched
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
||||
- name: user_id
|
||||
in: path
|
||||
required: true
|
||||
|
|
@ -486,42 +479,9 @@ paths:
|
|||
'500':
|
||||
description: Internal server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
- XsrfAuthHeader: []
|
||||
components:
|
||||
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:
|
||||
in: query
|
||||
name: cursor
|
||||
|
|
@ -780,3 +740,11 @@ components:
|
|||
Review:
|
||||
type: object
|
||||
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"
|
||||
schemas:
|
||||
$ref: "./schemas/_index.yaml"
|
||||
securitySchemes:
|
||||
$ref: "./securitySchemes/_index.yaml"
|
||||
|
|
@ -1,10 +1,4 @@
|
|||
cursor:
|
||||
$ref: "./cursor.yaml"
|
||||
title_sort:
|
||||
$ref: "./title_sort.yaml"
|
||||
accessToken:
|
||||
$ref: "./access_token.yaml"
|
||||
csrfToken:
|
||||
$ref: "./xsrf_token_cookie.yaml"
|
||||
csrfTokenHeader:
|
||||
$ref: "./xsrf_token_header.yaml"
|
||||
$ref: "./title_sort.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:
|
||||
summary: Get user title
|
||||
operationId: getUserTitle
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: user_id
|
||||
|
|
@ -37,9 +35,8 @@ patch:
|
|||
description: User updating title list of watched
|
||||
operationId: updateUserTitle
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
- XsrfAuthHeader: []
|
||||
parameters:
|
||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
||||
- in: path
|
||||
name: user_id
|
||||
required: true
|
||||
|
|
@ -87,9 +84,8 @@ delete:
|
|||
description: User deleting title from list of watched
|
||||
operationId: deleteUserTitle
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
- XsrfAuthHeader: []
|
||||
parameters:
|
||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
||||
- in: path
|
||||
name: user_id
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
get:
|
||||
summary: Get user info
|
||||
operationId: getUsersId
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: user_id
|
||||
|
|
@ -30,15 +28,15 @@ get:
|
|||
|
||||
patch:
|
||||
summary: Partially update a user account
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
description: |
|
||||
Update selected user profile fields (excluding password).
|
||||
Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||
Fields not provided in the request body remain unchanged.
|
||||
operationId: updateUser
|
||||
security:
|
||||
XsrfAuthHeader: []
|
||||
parameters:
|
||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
||||
# - $ref: '../parameters/xsrf_token_header.yaml'
|
||||
- name: user_id
|
||||
in: path
|
||||
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 { Header } from "./components/Header/Header";
|
||||
|
||||
import { OpenAPI } from "./api";
|
||||
|
||||
OpenAPI.WITH_CREDENTIALS = true
|
||||
|
||||
const App: React.FC = () => {
|
||||
const username = localStorage.getItem("username") || undefined;
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
|
|||
export { OpenAPI } 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 { CursorObj } from './models/CursorObj';
|
||||
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/`).
|
||||
* 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 requestBody
|
||||
* @returns User User updated successfully. Returns updated user representation (excluding sensitive fields).
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static updateUser(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
requestBody: {
|
||||
/**
|
||||
|
|
@ -175,9 +171,6 @@ export class DefaultService {
|
|||
path: {
|
||||
'user_id': userId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
|
|
@ -316,9 +309,6 @@ export class DefaultService {
|
|||
/**
|
||||
* Update a usertitle
|
||||
* 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 titleId
|
||||
* @param requestBody
|
||||
|
|
@ -326,7 +316,6 @@ export class DefaultService {
|
|||
* @throws ApiError
|
||||
*/
|
||||
public static updateUserTitle(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
titleId: number,
|
||||
requestBody: {
|
||||
|
|
@ -341,9 +330,6 @@ export class DefaultService {
|
|||
'user_id': userId,
|
||||
'title_id': titleId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
|
|
@ -358,16 +344,12 @@ export class DefaultService {
|
|||
/**
|
||||
* Delete a usertitle
|
||||
* 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 titleId
|
||||
* @returns any Title successfully deleted
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static deleteUserTitle(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
titleId: number,
|
||||
): CancelablePromise<any> {
|
||||
|
|
@ -378,9 +360,6 @@ export class DefaultService {
|
|||
'user_id': userId,
|
||||
'title_id': titleId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
errors: {
|
||||
401: `Unauthorized — missing or invalid auth token`,
|
||||
403: `Forbidden — user not allowed to delete title`,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { DefaultService } from "../../api";
|
||||
import type { UserTitleStatus } from "../../api";
|
||||
import { useCookies } from 'react-cookie';
|
||||
// import { useCookies } from 'react-cookie';
|
||||
|
||||
import {
|
||||
ClockIcon,
|
||||
|
|
@ -19,8 +19,8 @@ const STATUS_BUTTONS: { status: UserTitleStatus; icon: React.ReactNode; label: s
|
|||
];
|
||||
|
||||
export function TitleStatusControls({ titleId }: { titleId: number }) {
|
||||
const [cookies] = useCookies(['xsrf_token']);
|
||||
const xsrfToken = cookies['xsrf_token'] || null;
|
||||
// const [cookies] = useCookies(['xsrf_token']);
|
||||
// const xsrfToken = cookies['xsrf_token'] || null;
|
||||
|
||||
const [currentStatus, setCurrentStatus] = useState<UserTitleStatus | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
|
@ -46,7 +46,7 @@ export function TitleStatusControls({ titleId }: { titleId: number }) {
|
|||
try {
|
||||
// 1) Если кликнули на текущий статус — DELETE
|
||||
if (currentStatus === status) {
|
||||
await DefaultService.deleteUserTitle(xsrfToken, userId, titleId);
|
||||
await DefaultService.deleteUserTitle(userId, titleId);
|
||||
setCurrentStatus(null);
|
||||
return;
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ export function TitleStatusControls({ titleId }: { titleId: number }) {
|
|||
setCurrentStatus(added.status);
|
||||
} else {
|
||||
// уже есть запись — PATCH
|
||||
const updated = await DefaultService.updateUserTitle(xsrfToken, userId, titleId, { status });
|
||||
const updated = await DefaultService.updateUserTitle(userId, titleId, { status });
|
||||
setCurrentStatus(updated.status);
|
||||
}
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue