Skip to content

Commit 248f06e

Browse files
authored
Merge pull request #5490 from cisagov/feature/CSET-3493
User Settings changes for external authentication
2 parents a8d14cf + d836118 commit 248f06e

File tree

21 files changed

+199
-55
lines changed

21 files changed

+199
-55
lines changed

CSETWebApi/CSETWeb_Api/CSETWebCore.Business/AssessmentIO/Import/Importer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public int RunImportManualPortion(bool overwriteAssessment)
173173
refreshContact.Phone = originalContact.Phone;
174174
refreshContact.Title = originalContact.Title;
175175
//If they have removed this assessment on enterprise and this contact is being re-added, they are demoted to a user role.
176-
refreshContact.AssessmentRoleId = 1;
176+
refreshContact.AssessmentRoleId = Constants.Constants.UserRoleUser;
177177

178178
_context.ASSESSMENT_CONTACTS.Add(refreshContact);
179179
_context.SaveChanges();

CSETWebApi/CSETWeb_Api/CSETWebCore.Constants/Constants.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public static class Constants
1515

1616
public static string AssesmentUser = "ASSESS_USER";
1717

18+
public static int UserRoleUser = 1;
19+
public static int UserRoleAdmin = 2;
20+
1821
public static string Token_TimezoneOffsetKey = "tzoffset";
1922
public static string Token_UserId = "userid";
2023
public static string Token_AccessKey = "acckey";

CSETWebApi/CSETWeb_Api/CSETWebCore.Helpers/TokenManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ public void AuthorizeAdminRole()
586586
var myAdminConnections = _context.ASSESSMENT_CONTACTS.Where(
587587
ac => ac.UserId == userId
588588
&& ac.Assessment_Id == assessmentId
589-
&& ac.AssessmentRoleId == 2)
589+
&& ac.AssessmentRoleId == Constants.Constants.UserRoleAdmin)
590590
.ToList();
591591

592592
if (myAdminConnections.Count() == 0)
@@ -608,13 +608,13 @@ public bool AmILastAdminWithUsers(int assessmentId)
608608

609609
var adminConnections = _context.ASSESSMENT_CONTACTS.Where(
610610
ac => ac.Assessment_Id == assessmentId
611-
&& ac.AssessmentRoleId == 2)
611+
&& ac.AssessmentRoleId == Constants.Constants.UserRoleAdmin)
612612
.ToList();
613613

614614

615615
var userConnections = _context.ASSESSMENT_CONTACTS.Where(
616616
ac => ac.Assessment_Id == assessmentId
617-
&& ac.AssessmentRoleId == 1)
617+
&& ac.AssessmentRoleId == Constants.Constants.UserRoleUser)
618618
.ToList();
619619

620620
// Return a boolean indicating whether I am the last Admin and there is more than one User

CSETWebApi/CSETWeb_Api/CSETWebCore.Helpers/UserAuthentication.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ public async Task<LoginResponse> AuthenticateStandalone(Login login, ITokenManag
277277
FirstName = name,
278278
LastName = "",
279279
PrimaryEmail = primaryEmailSO,
280-
AssessmentRoleId = 2, // Admin role
280+
AssessmentRoleId = Constants.Constants.UserRoleAdmin, // Admin role
281281
Invited = true
282282
};
283283

@@ -419,9 +419,34 @@ public async Task<LoginResponse> ExchangeToken(ClaimsPrincipal user, string tzOf
419419
var email = user.FindFirstValue("email") ?? user.FindFirstValue(ClaimTypes.Email)
420420
?? throw new InvalidOperationException("Email claim not found in token.");
421421

422+
423+
422424
// Locate the CSET user record via the email claim
423-
var dbUser = _context.USERS.FirstOrDefault(x => x.PrimaryEmail == email)
424-
?? throw new UnauthorizedAccessException($"User email '{email}' is not registered in CSET.");
425+
var dbUser = _context.USERS.FirstOrDefault(x => x.PrimaryEmail == email);
426+
427+
428+
// if the user is not yet defined in CSET, create them
429+
if (!String.IsNullOrEmpty(email) && dbUser == null)
430+
{
431+
var givenName = user.FindFirstValue(ClaimTypes.GivenName);
432+
var surname = user.FindFirstValue(ClaimTypes.Surname);
433+
434+
dbUser = new USERS {
435+
PrimaryEmail = email,
436+
FirstName = givenName,
437+
LastName = surname
438+
};
439+
_context.USERS.Add(dbUser);
440+
_context.SaveChanges();
441+
442+
443+
var dbUserRole = new USER_ROLES {
444+
RoleId = Constants.Constants.UserRoleUser,
445+
UserId = dbUser.UserId
446+
};
447+
_context.USER_ROLES.Add(dbUserRole);
448+
_context.SaveChanges();
449+
}
425450

426451

427452
// Build response object

CSETWebApi/CSETWeb_Api/CSETWebCore.Model/Auth/AuthSettings.cs

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ public class AuthSettings
1212
public OidcSettings OIDC { get; set; }
1313
}
1414

15+
/// <summary>
16+
/// Represents the configuration settings required for integrating an application
17+
/// with an OpenID Connect (OIDC) identity provider.
18+
/// </summary>
1519
public class OidcSettings
1620
{
1721
/// <summary>
@@ -30,18 +34,41 @@ public class OidcSettings
3034
/// </summary>
3135
public string PostLogoutRedirectUri { get; set; } = string.Empty;
3236

37+
38+
/// <summary>
39+
/// The base URL of the OIDC identity provider (the realm/tenant issuer URL).
40+
/// The app will use this to discover provider endpoints via
41+
/// {issuer}/.well-known/openid-configuration.
42+
/// </summary>
43+
public string Issuer { get; set; } = string.Empty;
44+
45+
/// <summary>
46+
/// The base URL used by the application to interact with the OIDC identity provider.
47+
/// It serves as the starting point for discovering metadata and endpoints (e.g., authorization,
48+
/// token, and user information endpoints) via the OIDC discovery document.
49+
/// Typically, this matches the Issuer value but is explicitly included for configuration purposes.
50+
/// </summary>
51+
public string Authority { get; set; } = string.Empty;
52+
53+
/// <summary>
54+
/// Identifies the client ID of the application registered with the OIDC provider.
55+
/// This value must match the "aud" (audience) claim in the token to validate it.
56+
/// </summary>
57+
public string Audience { get; set; } = string.Empty;
58+
59+
3360
/// <summary>
3461
/// Space-separated list of OAuth 2.0 scopes to request.
3562
/// openid is required; profile and email are standard optional scopes.
3663
/// </summary>
3764
public string Scope { get; set; } = string.Empty;
3865

3966
/// <summary>
40-
///The base URL of the OIDC identity provider (the realm/tenant issuer URL).
41-
///The app will use this to discover provider endpoints via
42-
///{issuer}/.well-known/openid-configuration.
67+
/// Specifies the claim name in the token that should be used to
68+
/// identify the username. Defaults to "preferred_username" if not specified.
4369
/// </summary>
44-
public string Issuer { get; set; } = string.Empty;
70+
public string ClaimUsernameProperty { get; set; } = string.Empty;
71+
4572

4673
/// <summary>
4774
/// When true, enforces that all endpoint URLs in the discovery document
@@ -57,22 +84,11 @@ public class OidcSettings
5784
/// </summary>
5885
public bool RequireHttps { get; set; } = true;
5986

87+
6088
/// <summary>
6189
/// When true, logs verbose OIDC debug output to the browser console.
6290
/// Useful during development and troubleshooting. Should be false in production.
6391
/// </summary>
6492
public bool ShowDebugInformation { get; set; } = false;
65-
66-
/// <summary>
67-
/// Identifies the client ID of the application registered with the OIDC provider.
68-
/// This value must match the "aud" (audience) claim in the token to validate it.
69-
/// </summary>
70-
public string Audience { get; set; } = string.Empty;
71-
72-
/// <summary>
73-
/// Specifies the claim name in the token that should be used to
74-
/// identify the username. Defaults to "preferred_username" if not specified.
75-
/// </summary>
76-
public string ClaimUsernameProperty { get; set; } = string.Empty;
7793
}
7894
}

CSETWebApi/CSETWeb_Api/CSETWeb_ApiCore/Startup.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
using Microsoft.IdentityModel.Tokens;
7171
using Microsoft.OpenApi.Models;
7272
using Newtonsoft.Json;
73+
using NLog;
7374
using System;
7475
using System.IO;
7576
using System.Linq;
@@ -143,24 +144,24 @@ public void ConfigureServices(IServiceCollection services)
143144
// Configure OIDC authentication if configured to do so
144145
var authSettings = Configuration.GetSection("Auth").Get<AuthSettings>();
145146

146-
if (authSettings.OIDC?.Issuer != null)
147+
if (authSettings.OIDC?.Authority != null)
147148
{
148149
authBuilder.AddJwtBearer("OIDC", options =>
149150
{
150-
options.Authority = authSettings.OIDC.Issuer;
151+
options.Authority = authSettings.OIDC.Authority;
151152
options.Audience = authSettings.OIDC.Audience;
152-
options.RequireHttpsMetadata = !_env.IsDevelopment();
153+
options.RequireHttpsMetadata = authSettings.OIDC.RequireHttps;
153154

154155
options.Events = new JwtBearerEvents
155156
{
156157
OnAuthenticationFailed = ctx =>
157158
{
158-
Console.WriteLine($"OIDC auth failed: {ctx.Exception}");
159+
LogManager.GetCurrentClassLogger().Error($"OIDC auth failed: {ctx.Exception}");
159160
return Task.CompletedTask;
160161
},
161162
OnTokenValidated = ctx =>
162163
{
163-
Console.WriteLine("OIDC token validated successfully");
164+
LogManager.GetCurrentClassLogger().Error("OIDC token validated successfully");
164165
return Task.CompletedTask;
165166
}
166167
};

CSETWebApi/CSETWeb_Api/CSETWeb_ApiCore/appsettings.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,24 @@
3838
]
3939
},
4040
"Auth": {
41-
"x_____OIDC": {
42-
"Note": "rename this section to 'oidc' to enable external authentication",
41+
"disabled_OIDC": {
42+
"Note": "rename this section key to 'OIDC' to enable external authentication",
43+
4344
"ClientId": "cset-local",
4445
"RedirectUri": "https://{your-application-host}/callback",
4546
"PostLogoutRedirectUri": "http://{your-application-host}",
46-
"Scope": "openid profile email",
47+
4748
"Issuer": "https://{oidc-provider-host}/realms/{realm}",
49+
"Authority": "https://{oidc-provider-host}/realms/{realm}",
50+
"Audience": "account",
51+
52+
"Scope": "openid profile email",
53+
"ClaimUsernameProperty": "preferred_username",
54+
4855
"StrictDiscoveryDocumentValidation": false,
4956
"RequireHttps": false,
50-
"ShowDebugInformation": true,
5157

52-
"Audience": "account",
53-
"ClaimUsernameProperty": "preferred_username"
58+
"ShowDebugInformation": true
5459
}
5560
}
5661
}

CSETWebApi/CSETWeb_Api/DuplicateAssessments/MockTokenManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ public void AuthorizeAdminRole()
547547
var myAdminConnections = _context.ASSESSMENT_CONTACTS.Where(
548548
ac => ac.UserId == userId
549549
&& ac.Assessment_Id == assessmentId
550-
&& ac.AssessmentRoleId == 2)
550+
&& ac.AssessmentRoleId == Constants.UserRoleAdmin)
551551
.ToList();
552552

553553
if (myAdminConnections.Count() == 0)
@@ -569,13 +569,13 @@ public bool AmILastAdminWithUsers(int assessmentId)
569569

570570
var adminConnections = _context.ASSESSMENT_CONTACTS.Where(
571571
ac => ac.Assessment_Id == assessmentId
572-
&& ac.AssessmentRoleId == 2)
572+
&& ac.AssessmentRoleId == Constants.UserRoleAdmin)
573573
.ToList();
574574

575575

576576
var userConnections = _context.ASSESSMENT_CONTACTS.Where(
577577
ac => ac.Assessment_Id == assessmentId
578-
&& ac.AssessmentRoleId == 1)
578+
&& ac.AssessmentRoleId == Constants.UserRoleAdmin)
579579
.ToList();
580580

581581
// Return a boolean indicating whether I am the last Admin and there is more than one User

CSETWebNg/src/app/callback/callback.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ export class CallbackComponent implements OnInit {
4848

4949

5050
const accessToken = this.authExtSvc.oauthService.getAccessToken();
51+
const idToken = this.authExtSvc.oauthService.getIdToken();
52+
sessionStorage.setItem('oidc-token', accessToken);
53+
sessionStorage.setItem('oidc-id-token', idToken);
5154

5255

5356
// Send token to your backend

CSETWebNg/src/app/dialogs/change-password/change-password.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ export class ChangePasswordComponent implements OnInit {
147147
// if canceling out of a change TEMP password, navigate back to the login
148148
// but if cancelling out of a NON-TEMP password change, don't do anything.
149149
if (this.warning && this.message === this.msgChangeTempPw) {
150-
this.auth.logout();
150+
this.auth.logOut();
151151
}
152152
}
153153

0 commit comments

Comments
 (0)