Skip to content

Commit c99814f

Browse files
committed
feat: implement anti-forgery token handling in API and frontend
1 parent a5cdfc6 commit c99814f

7 files changed

Lines changed: 60 additions & 3 deletions

File tree

.github/workflows/codeql-analysis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343

4444
# Initializes the CodeQL tools for scanning.
4545
- name: Initialize CodeQL
46-
uses: github/codeql-action/init@v3
46+
uses: github/codeql-action/init@v4
4747
with:
4848
languages: ${{ matrix.language }}
4949
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,7 +54,7 @@ jobs:
5454
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
5555
# If this step fails, then you should remove it and run the build manually (see below)
5656
- name: Autobuild
57-
uses: github/codeql-action/autobuild@v3
57+
uses: github/codeql-action/autobuild@v4
5858

5959
# ℹ️ Command-line programs to run using the OS shell.
6060
# 📚 https://git.io/JvXDl
@@ -68,4 +68,4 @@ jobs:
6868
# make release
6969

7070
- name: Perform CodeQL Analysis
71-
uses: github/codeql-action/analyze@v3
71+
uses: github/codeql-action/analyze@v4

src/IdentityManager2/Api/Controllers/RolesController.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public async Task<IActionResult> GetRolesAsync(string filter = null, int start =
6868

6969
// POST
7070
[HttpPost, Route("", Name = IdentityManagerConstants.RouteNames.CreateRole)]
71+
[ValidateAntiForgeryToken]
7172
public async Task<IActionResult> CreateRoleAsync([FromBody] PropertyValue[] properties)
7273
{
7374
var meta = await GetMetadataAsync();
@@ -140,6 +141,7 @@ public async Task<IActionResult> GetRoleAsync(string subject)
140141
}
141142

142143
[HttpDelete, Route("{subject}", Name = IdentityManagerConstants.RouteNames.DeleteRole)]
144+
[ValidateAntiForgeryToken]
143145
public async Task<IActionResult> DeleteRoleAsync(string subject)
144146
{
145147
var meta = await GetMetadataAsync();
@@ -163,6 +165,7 @@ public async Task<IActionResult> DeleteRoleAsync(string subject)
163165
}
164166

165167
[HttpPut, Route("{subject}/properties/{type}", Name = IdentityManagerConstants.RouteNames.UpdateRoleProperty)]
168+
[ValidateAntiForgeryToken]
166169
public async Task<IActionResult> SetPropertyAsync(string subject, string type)
167170
{
168171
if (IsNullOrWhiteSpace(subject))

src/IdentityManager2/Api/Controllers/UsersController.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public async Task<IActionResult> GetUsersAsync(string filter = null, int start =
5555
}
5656

5757
[HttpPost("", Name = IdentityManagerConstants.RouteNames.CreateUser)]
58+
[ValidateAntiForgeryToken]
5859
public async Task<IActionResult> CreateUserAsync([FromBody] PropertyValue[] properties)
5960
{
6061
var meta = await GetMetadataAsync();
@@ -135,6 +136,7 @@ public async Task<IActionResult> GetUserAsync(string subject)
135136
}
136137

137138
[HttpDelete, Route("{subject}", Name = IdentityManagerConstants.RouteNames.DeleteUser)]
139+
[ValidateAntiForgeryToken]
138140
public async Task<IActionResult> DeleteUserAsync(string subject)
139141
{
140142
var meta = await GetMetadataAsync();
@@ -164,6 +166,7 @@ public async Task<IActionResult> DeleteUserAsync(string subject)
164166
}
165167

166168
[HttpPut, Route("{subject}/properties/{type}", Name = IdentityManagerConstants.RouteNames.UpdateUserProperty)]
169+
[ValidateAntiForgeryToken]
167170
public async Task<IActionResult> SetPropertyAsync(string subject, string type)
168171
{
169172
if (IsNullOrWhiteSpace(subject))
@@ -194,6 +197,7 @@ public async Task<IActionResult> SetPropertyAsync(string subject, string type)
194197
}
195198

196199
[HttpPost, Route("{subject}/claims", Name = IdentityManagerConstants.RouteNames.AddClaim)]
200+
[ValidateAntiForgeryToken]
197201
public async Task<IActionResult> AddClaimAsync(string subject, [FromBody] ClaimValue model)
198202
{
199203
var meta = await GetMetadataAsync();
@@ -229,6 +233,7 @@ public async Task<IActionResult> AddClaimAsync(string subject, [FromBody] ClaimV
229233
}
230234

231235
[HttpDelete, Route("{subject}/claims/{type}/{value}", Name = IdentityManagerConstants.RouteNames.RemoveClaim)]
236+
[ValidateAntiForgeryToken]
232237
public async Task<IActionResult> RemoveClaimAsync(string subject, string type, string value)
233238
{
234239
type = type.FromBase64UrlEncoded();
@@ -257,6 +262,7 @@ public async Task<IActionResult> RemoveClaimAsync(string subject, string type, s
257262
}
258263

259264
[HttpPost, Route("{subject}/roles/{role}", Name = IdentityManagerConstants.RouteNames.AddRole)]
265+
[ValidateAntiForgeryToken]
260266
public async Task<IActionResult> AddRoleAsync(string subject, string role)
261267
{
262268
var meta = await GetMetadataAsync();
@@ -282,6 +288,7 @@ public async Task<IActionResult> AddRoleAsync(string subject, string role)
282288
}
283289

284290
[HttpDelete, Route("{subject}/roles/{role}", Name = IdentityManagerConstants.RouteNames.RemoveRole)]
291+
[ValidateAntiForgeryToken]
285292
public async Task<IActionResult> RemoveRoleAsync(string subject, string role)
286293
{
287294
var meta = await GetMetadataAsync();

src/IdentityManager2/Areas/IdentityManager/Pages/Index.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<link rel="shortcut icon" type="image/x-icon" href="@Model.PathBase/assets/Content.favicon.ico" />
1414
</head>
1515
<body lang="en" ng-app="ttIdmApp" ng-csp ng-controller="LayoutCtrl" ng-cloak>
16+
@Html.AntiForgeryToken()
1617

1718
<div ng-include="'@Model.PathBase/assets/Templates.navbar.html'"></div>
1819

src/IdentityManager2/Assets/Scripts/App/ttIdm.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@
1111
return {
1212
'request': function(config) {
1313
idmErrorService.clear();
14+
15+
// Add anti-forgery token for non-GET requests
16+
if (config.method && config.method !== 'GET') {
17+
try {
18+
const rvt = document.querySelector('input[name="__RequestVerificationToken"]');
19+
if (rvt) {
20+
const token = rvt.value;
21+
if (token) {
22+
if (!config.headers) {
23+
config.headers = {};
24+
}
25+
config.headers.RequestVerificationToken = token;
26+
}
27+
}
28+
} catch (e) {
29+
console.error("Failed to extract antiforgery token:", e);
30+
}
31+
}
32+
1433
return config;
1534
},
1635
'responseError': function(response) {

src/IdentityManager2/Assets/Scripts/Bundle.js

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/IdentityManager2/Configuration/DependencyInjection/IdentityManagerServiceCollectionExtensions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ public static IIdentityManagerBuilder AddIdentityManager(this IServiceCollection
1919
var identityManagerOptions = services.BuildServiceProvider().GetRequiredService<IOptions<IdentityManagerOptions>>().Value;
2020
identityManagerOptions.Validate();
2121

22+
services.AddAntiforgery(options =>
23+
{
24+
// options.HeaderName = "X-XSRF-TOKEN";
25+
// options.Cookie.Name = "XSRF-TOKEN";
26+
options.Cookie.HttpOnly = false;
27+
options.Cookie.SameSite = SameSiteMode.Strict;
28+
});
29+
2230
services.AddControllersWithViews()
2331
.AddJsonOptions(static options =>
2432
{

0 commit comments

Comments
 (0)