Skip to content

Commit

Permalink
refactored eventloghelper, authenticationhelper and updated logging i…
Browse files Browse the repository at this point in the history
…mplementation and tests
  • Loading branch information
acn-dgopa committed Sep 12, 2023
1 parent d635342 commit cbf7829
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 127 deletions.
17 changes: 8 additions & 9 deletions src/Authentication/Controllers/AuthenticationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public AuthenticationController(
[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];
Expand Down Expand Up @@ -193,6 +194,7 @@ public async Task<ActionResult> AuthenticateUser([FromQuery] string goTo, [FromQ
}

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 @@ public async Task<ActionResult> AuthenticateUser([FromQuery] string goTo, [FromQ
}
}

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

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 / 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 / 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,8 +269,7 @@ public async Task<ActionResult> RefreshJwtCookie()

string serializedToken = await GenerateToken(principal);

UserAuthenticationModel userAuthentication = AuthenticationHelper.GetUserFromToken(serializedToken, null);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, userAuthentication, AuthenticationEventType.Refresh);
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 (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 / 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 / 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 @@ -367,8 +369,7 @@ private async Task<ActionResult> AuthenticateAltinnStudioToken(string originalTo
ClaimsPrincipal principal = new ClaimsPrincipal(identity);

string serializedToken = await GenerateToken(principal);
UserAuthenticationModel userAuthentication = AuthenticationHelper.GetUserFromToken(serializedToken, null);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, userAuthentication, AuthenticationEventType.TokenExchange);
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 (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 / 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 / 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 @@ -488,8 +489,7 @@ private async Task<ActionResult> AuthenticateMaskinportenToken(string originalTo
ClaimsPrincipal principal = new ClaimsPrincipal(identity);

string serializedToken = await GenerateToken(principal);
UserAuthenticationModel userAuthentication = AuthenticationHelper.GetUserFromToken(serializedToken, null);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, userAuthentication, AuthenticationEventType.TokenExchange);
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 (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 / 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 / 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 @@ -623,8 +623,7 @@ private async Task<ActionResult> AuthenticateIdPortenToken(string originalToken)
ClaimsPrincipal principal = new ClaimsPrincipal(identity);

string serializedToken = await GenerateToken(principal, token.ValidTo);
UserAuthenticationModel userAuthentication = AuthenticationHelper.GetUserFromToken(serializedToken, null);
EventlogHelper.CreateAuthenticationEvent(_featureManager, _eventLog, userAuthentication, AuthenticationEventType.TokenExchange);
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 (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 / 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 / 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
6 changes: 2 additions & 4 deletions src/Authentication/Controllers/LogoutController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public LogoutController(
[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 @@ public ActionResult Logout()
}

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 (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 / 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 / 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 (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 / 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 / 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 Down
147 changes: 69 additions & 78 deletions src/Authentication/Helpers/AuthenticationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,103 +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;
}

if (userAuthenticationModel.AuthenticationMethod == AuthenticationMethod.NotDefined)
// Altinn Specific claims
if (claim.Type.Equals(AltinnCoreClaimTypes.UserId))
{
userAuthenticationModel.AuthenticationMethod = (AuthenticationMethod)System.Enum.Parse(typeof(AuthenticationMethod), provider?.DefaultAuthenticationMethod);
userAuthenticationModel.UserID = Convert.ToInt32(claim.Value);
continue;
}
}

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

/// <summary>
/// Get user information from the serializwd token string
/// </summary>
/// <param name="jwtToken">serialized jwt token string</param>
/// <param name="provider">token provider</param>
/// <returns></returns>
public static UserAuthenticationModel GetUserFromToken(string jwtToken, OidcProvider provider)
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
if (!string.IsNullOrEmpty(jwtToken))
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 (claim.Type.Equals("amr"))
{
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)
{
JwtSecurityToken token = tokenHandler.ReadJwtToken(jwtToken);
return GetUserFromToken(token, provider);
userAuthenticationModel.AuthenticationMethod = (AuthenticationMethod)System.Enum.Parse(typeof(AuthenticationMethod), provider.DefaultAuthenticationMethod);
}

return null;
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 @@ -138,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 Down
87 changes: 74 additions & 13 deletions src/Authentication/Helpers/EventlogHelper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Altinn.Platform.Authentication.Configuration;
using Altinn.Platform.Authentication.Enum;
using Altinn.Platform.Authentication.Model;
using Altinn.Platform.Authentication.Services.Interfaces;
using AltinnCore.Authentication.Constants;
using Microsoft.FeatureManagement;

namespace Altinn.Platform.Authentication.Helpers
Expand All @@ -17,22 +23,77 @@ public static class EventlogHelper
/// </summary>
/// <param name="featureManager">handler for feature manager service</param>
/// <param name="eventLog">handler for eventlog service</param>
/// <param name="userAuthentication">authentication object</param>
public async static Task CreateAuthenticationEvent(IFeatureManager featureManager, IEventLog eventLog, UserAuthenticationModel userAuthentication, AuthenticationEventType? eventType)
{
if (eventType != null)
{
userAuthentication.EventType = eventType;
}
else
/// <param name="jwtToken">token in the authentication request</param>
/// <param name="eventType">authentication event type</param>
public async static Task CreateAuthenticationEvent(IFeatureManager featureManager, IEventLog eventLog, string jwtToken, AuthenticationEventType eventType)
{
if (await featureManager.IsEnabledAsync(FeatureFlags.AuditLog))
{
userAuthentication.EventType = userAuthentication.IsAuthenticated ? AuthenticationEventType.Authenticated : AuthenticationEventType.AuthenticationFailed;
AuthenticationEvent authenticationEvent = MapAuthenticationEventFromToken(jwtToken, eventType);
eventLog.CreateAuthenticationEvent(authenticationEvent);
}

if (await featureManager.IsEnabledAsync(FeatureFlags.AuditLog))
}

/// <summary>
/// Maps claims to the authentication event model
/// </summary>
/// <param name="jwtToken">authenticated token</param>
/// <param name="eventType">authentication event type</param>
/// <returns>authentication event</returns>
public static AuthenticationEvent MapAuthenticationEventFromToken(string jwtToken, AuthenticationEventType eventType)
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
AuthenticationEvent authenticationEvent = null;
if (!string.IsNullOrEmpty(jwtToken))
{
eventLog.CreateAuthenticationEvent(userAuthentication);
JwtSecurityToken token = tokenHandler.ReadJwtToken(jwtToken);

if (token != null)
{
authenticationEvent = new AuthenticationEvent();
foreach (Claim claim in token.Claims)
{
// Handle various claim types
switch (claim.Type)
{
case AltinnCoreClaimTypes.UserId:
authenticationEvent.UserId = claim.Value;
break;

case AltinnCoreClaimTypes.Org:
authenticationEvent.OrgNumber = claim.Value;
break;

case AltinnCoreClaimTypes.OrgNumber:
authenticationEvent.OrgNumber = claim.Value;
break;

case AltinnCoreClaimTypes.AuthenticateMethod:
AuthenticationMethod authenticationMethod;
authenticationEvent.AuthenticationMethod = System.Enum.TryParse<AuthenticationMethod>(claim.Value, false, out authenticationMethod) ? authenticationMethod.ToString() : AuthenticationMethod.NotDefined.ToString();
break;

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

case "amr":
authenticationEvent.AuthenticationMethod = AuthenticationHelper.GetAuthenticationMethod(claim.Value).ToString();
break;

case "acr":
authenticationEvent.AuthenticationLevel = AuthenticationHelper.GetAuthenticationLevel(claim.Value).ToString();
break;
}
}

authenticationEvent.EventType = eventType.ToString();
}

return authenticationEvent;
}

return authenticationEvent;
}
}
}
Loading

0 comments on commit cbf7829

Please sign in to comment.