Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/implement evntlog refresh #316

Merged
merged 11 commits into from
Sep 21, 2023
9 changes: 8 additions & 1 deletion src/Authentication/Controllers/AuthenticationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,13 @@
[HttpGet("authentication")]
public async Task<ActionResult> AuthenticateUser([FromQuery] string goTo, [FromQuery] bool dontChooseReportee)
{
string originalToken = null;
if (string.IsNullOrEmpty(goTo) && HttpContext.Request.Cookies[_generalSettings.AuthnGoToCookieName] != null)
{
goTo = HttpContext.Request.Cookies[_generalSettings.AuthnGoToCookieName];
}

if (!Uri.TryCreate(goTo, UriKind.Absolute, out Uri goToUri) || !IsValidRedirectUri(goToUri.Host))

Check failure

Code scanning / CodeQL

User-controlled bypass of sensitive method High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
This condition guards a sensitive
action
, but a
user-provided value
controls it.
This condition guards a sensitive
action
, but a
user-provided value
controls it.
This condition guards a sensitive
action
, but a
user-provided value
controls it.

Check failure

Code scanning / CodeQL

User-controlled bypass of sensitive method High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
This condition guards a sensitive
action
, but a
user-provided value
controls it.
This condition guards a sensitive
action
, but a
user-provided value
controls it.
This condition guards a sensitive
action
, but a
user-provided value
controls it.

Check failure

Code scanning / CodeQL

User-controlled bypass of sensitive method High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
This condition guards a sensitive
action
, but a
user-provided value
controls it.
{
return Redirect($"{_generalSettings.BaseUrl}");
}
Expand Down Expand Up @@ -193,6 +194,7 @@
}

OidcCodeResponse oidcCodeResponse = await _oidcProvider.GetTokens(code, provider, GetRedirectUri(provider));
originalToken = oidcCodeResponse.IdToken;
JwtSecurityToken jwtSecurityToken = await ValidateAndExtractOidcToken(oidcCodeResponse.IdToken, provider.WellKnownConfigEndpoint);
userAuthentication = AuthenticationHelper.GetUserFromToken(jwtSecurityToken, provider);
if (!ValidateNonce(HttpContext, userAuthentication.Nonce))
Expand Down Expand Up @@ -237,7 +239,8 @@
}
}

EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, userAuthentication, null);
AuthenticationEventType eventType = (userAuthentication != null && userAuthentication.IsAuthenticated) ? AuthenticationEventType.Authenticated : AuthenticationEventType.AuthenticationFailed;
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, userAuthentication, eventType);

Check warning on line 243 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 243 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 243 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

if (userAuthentication != null && userAuthentication.IsAuthenticated)
{
Expand Down Expand Up @@ -266,6 +269,7 @@

string serializedToken = await GenerateToken(principal);

EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, serializedToken, AuthenticationEventType.Refresh);

Check warning on line 272 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 272 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 272 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
_logger.LogInformation("End of refreshing token");

return Ok(serializedToken);
Expand Down Expand Up @@ -365,6 +369,7 @@
ClaimsPrincipal principal = new ClaimsPrincipal(identity);

string serializedToken = await GenerateToken(principal);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, serializedToken, AuthenticationEventType.TokenExchange);

Check warning on line 372 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 372 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 372 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
return Ok(serializedToken);
}
catch (Exception ex)
Expand Down Expand Up @@ -484,6 +489,7 @@
ClaimsPrincipal principal = new ClaimsPrincipal(identity);

string serializedToken = await GenerateToken(principal);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, serializedToken, AuthenticationEventType.TokenExchange);

Check warning on line 492 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 492 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 492 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
return Ok(serializedToken);
}
catch (Exception ex)
Expand Down Expand Up @@ -617,6 +623,7 @@
ClaimsPrincipal principal = new ClaimsPrincipal(identity);

string serializedToken = await GenerateToken(principal, token.ValidTo);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, serializedToken, AuthenticationEventType.TokenExchange);

Check warning on line 626 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 626 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 626 in src/Authentication/Controllers/AuthenticationController.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
return Ok(serializedToken);
}
catch (Exception ex)
Expand Down
8 changes: 4 additions & 4 deletions src/Authentication/Controllers/LogoutController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
[HttpGet("logout")]
public ActionResult Logout()
{
UserAuthenticationModel userAuthentication;
JwtSecurityToken jwt = null;
string orgIss = null;
string tokenCookie = Request.Cookies[_generalSettings.JwtCookieName];
Expand All @@ -71,18 +70,17 @@
}

OidcProvider provider = GetOidcProvider(orgIss);
userAuthentication = AuthenticationHelper.GetUserFromToken(jwt, provider);
if (provider == null)
{
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, userAuthentication, AuthenticationEventType.Logout);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, tokenCookie, AuthenticationEventType.Logout);

Check warning on line 75 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 75 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 75 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
return Redirect(_generalSettings.SBLLogoutEndpoint);
}

CookieOptions opt = new CookieOptions() { Domain = _generalSettings.HostName, Secure = true, HttpOnly = true };
Response.Cookies.Delete(_generalSettings.SblAuthCookieName, opt);
Response.Cookies.Delete(_generalSettings.JwtCookieName, opt);

EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, userAuthentication, AuthenticationEventType.Logout);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, tokenCookie, AuthenticationEventType.Logout);

Check warning on line 83 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 83 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 83 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
return Redirect(provider.LogoutEndpoint);
}

Expand All @@ -98,6 +96,8 @@
CookieOptions opt = new CookieOptions() { Domain = _generalSettings.HostName, Secure = true, HttpOnly = true };
Response.Cookies.Delete(_generalSettings.SblAuthCookieName, opt);
Response.Cookies.Delete(_generalSettings.JwtCookieName, opt);
string tokenCookie = Request.Cookies[_generalSettings.JwtCookieName];
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, tokenCookie, AuthenticationEventType.Logout);

Check warning on line 100 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Analyze

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 100 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 100 in src/Authentication/Controllers/LogoutController.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
return Ok();
}

Expand Down
4 changes: 3 additions & 1 deletion src/Authentication/Enum/AuthenticationEventType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public enum AuthenticationEventType
{
AuthenticationFailed,
Authenticated,
Logout
Refresh,
TokenExchange,
Logout,
}
}
12 changes: 11 additions & 1 deletion src/Authentication/Enum/AuthenticationMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ public enum AuthenticationMethod
/// <summary>
/// User is logged in with help of IDPORTEN OTC
/// </summary>
MinIDOTC = 15
MinIDOTC = 15,

/// <summary>
/// user is logged in with the help of maskinporten token
/// </summary>
MaskinPorten = 16,

/// <summary>
/// user is logged in with the help of virksomhets bruker
/// </summary>
VirksomhetsBruker = 17,
}
}
135 changes: 73 additions & 62 deletions src/Authentication/Helpers/AuthenticationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,85 +26,94 @@ public static UserAuthenticationModel GetUserFromToken(JwtSecurityToken jwtSecur
{
IsAuthenticated = true,
ProviderClaims = new Dictionary<string, List<string>>(),
Iss = provider?.IssuerKey,
Iss = provider.IssuerKey,
AuthenticationMethod = AuthenticationMethod.NotDefined
};

if (jwtSecurityToken != null)
foreach (Claim claim in jwtSecurityToken.Claims)
{
foreach (Claim claim in jwtSecurityToken.Claims)
// General OIDC claims
if (claim.Type.Equals("nonce"))
{
// Handle various claim types
switch (claim.Type)
{
// General OIDC claims
case "nonce":
userAuthenticationModel.Nonce = claim.Value;
break;

// Altinn Specific claims
case AltinnCoreClaimTypes.UserId:
userAuthenticationModel.UserID = Convert.ToInt32(claim.Value);
break;

case AltinnCoreClaimTypes.PartyID:
userAuthenticationModel.PartyID = Convert.ToInt32(claim.Value);
break;

case AltinnCoreClaimTypes.AuthenticateMethod:
userAuthenticationModel.AuthenticationMethod = (AuthenticationMethod)System.Enum.Parse(typeof(AuthenticationMethod), claim.Value);
break;

case AltinnCoreClaimTypes.AuthenticationLevel:
userAuthenticationModel.AuthenticationLevel = (SecurityLevel)System.Enum.Parse(typeof(SecurityLevel), claim.Value);
break;

// ID-porten specific claims
case "pid":
userAuthenticationModel.SSN = claim.Value;
break;

case "amr":
userAuthenticationModel.AuthenticationMethod = GetAuthenticationMethod(claim.Value);
break;

case "acr":
userAuthenticationModel.AuthenticationLevel = GetAuthenticationLevel(claim.Value);
break;

default:
// Check for external identity claim
if (!string.IsNullOrEmpty(provider?.ExternalIdentityClaim) && claim.Type.Equals(provider?.ExternalIdentityClaim))
{
userAuthenticationModel.ExternalIdentity = claim.Value;
}

// General claims handling
if (provider?.ProviderClaims != null && provider.ProviderClaims.Contains(claim.Type))
{
userAuthenticationModel.ProviderClaims.TryAdd(claim.Type, new List<string>());
userAuthenticationModel.ProviderClaims[claim.Type].Add(claim.Value);
}

break;
}
userAuthenticationModel.Nonce = claim.Value;
continue;
}

// Altinn Specific claims
if (claim.Type.Equals(AltinnCoreClaimTypes.UserId))
{
userAuthenticationModel.UserID = Convert.ToInt32(claim.Value);
continue;
}

if (claim.Type.Equals(AltinnCoreClaimTypes.PartyID))
{
userAuthenticationModel.PartyID = Convert.ToInt32(claim.Value);
continue;
}

if (claim.Type.Equals(AltinnCoreClaimTypes.AuthenticateMethod))
{
userAuthenticationModel.AuthenticationMethod = (Enum.AuthenticationMethod)System.Enum.Parse(typeof(Enum.AuthenticationMethod), claim.Value);
continue;
}

if (claim.Type.Equals(AltinnCoreClaimTypes.AuthenticationLevel))
{
userAuthenticationModel.AuthenticationLevel = (Enum.SecurityLevel)System.Enum.Parse(typeof(Enum.SecurityLevel), claim.Value);
continue;
}

// ID-porten specific claims
if (claim.Type.Equals("pid"))
{
userAuthenticationModel.SSN = claim.Value;
continue;
}

if (userAuthenticationModel.AuthenticationMethod == AuthenticationMethod.NotDefined)
if (claim.Type.Equals("amr"))
{
userAuthenticationModel.AuthenticationMethod = (AuthenticationMethod)System.Enum.Parse(typeof(AuthenticationMethod), provider?.DefaultAuthenticationMethod);
userAuthenticationModel.AuthenticationMethod = GetAuthenticationMethod(claim.Value);
continue;
}

if (claim.Type.Equals("acr"))
{
userAuthenticationModel.AuthenticationLevel = GetAuthenticationLevel(claim.Value);
continue;
}

if (!string.IsNullOrEmpty(provider.ExternalIdentityClaim) && claim.Type.Equals(provider.ExternalIdentityClaim))
{
userAuthenticationModel.ExternalIdentity = claim.Value;
}

// General claims handling
if (provider.ProviderClaims != null && provider.ProviderClaims.Contains(claim.Type))
{
if (!userAuthenticationModel.ProviderClaims.ContainsKey(claim.Type))
{
userAuthenticationModel.ProviderClaims.Add(claim.Type, new List<string>());
}

userAuthenticationModel.ProviderClaims[claim.Type].Add(claim.Value);
}
}

if (userAuthenticationModel.AuthenticationMethod == AuthenticationMethod.NotDefined)
{
userAuthenticationModel.AuthenticationMethod = (AuthenticationMethod)System.Enum.Parse(typeof(AuthenticationMethod), provider.DefaultAuthenticationMethod);
}

return userAuthenticationModel;
}

/// <summary>
/// Converts IDporten acr claim �Authentication Context Class Reference� - The security level of assurance for the
/// authentication. Possible values are Level3 (i.e. MinID was used) or Level4 (other eIDs).
/// The level must be validated by the client.
/// </summary>
private static SecurityLevel GetAuthenticationLevel(string acr)
public static SecurityLevel GetAuthenticationLevel(string acr)
{
switch (acr)
{
Expand All @@ -120,7 +129,7 @@ private static SecurityLevel GetAuthenticationLevel(string acr)
/// <summary>
/// Converts external methods to internal Minid-PIN, Minid-OTC, Commfides, Buypass, BankID, BankID Mobil or eIDAS
/// </summary>
private static AuthenticationMethod GetAuthenticationMethod(string amr)
public static AuthenticationMethod GetAuthenticationMethod(string amr)
{
switch (amr)
{
Expand All @@ -138,6 +147,8 @@ private static AuthenticationMethod GetAuthenticationMethod(string amr)
return Enum.AuthenticationMethod.BankIDMobil;
case "eIDAS":
return Enum.AuthenticationMethod.EIDAS;
case "maskinporten":
return Enum.AuthenticationMethod.MaskinPorten;
}

return Enum.AuthenticationMethod.NotDefined;
Expand Down
Loading
Loading