Why is my Azure Identity Experience Framework policy issuing a JwtToken with alg HS256 instead of RS256? - authentication

TechnicalProfile looks like this:
<TechnicalProfile Id="AAD-Common">
<DisplayName>Multi-Tenant AAD</DisplayName>
<Description>Login with your Contoso account</Description>
<Protocol Name="OpenIdConnect"/>
<Metadata>
<Item Key="METADATA">https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration</Item>
<!-- Update the Client ID below to the Application ID -->
<Item Key="client_id">[the client id]</Item>
<Item Key="response_types">id_token</Item>
<Item Key="scope">openid</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<!-- The commented key below specifies that users from any tenant can sign-in. Uncomment if you would like anyone with an Azure AD account to be able to sign in. -->
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_AADAppSecret"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid"/>
<OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid"/>
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin"/>
</TechnicalProfile>
When I validate this bearer token using Microsoft.AspNetCore.Authentication.JwtBearer AddJwtBearer I get this error:
token: '{"alg":"HS256","typ":"JWT","kid":"TwMsJMU3i_L7zOBKeOw7nWYHov3-eY70IsPfs1gT3aU"}.
{"exp":1588321433,"nbf":1588317833,"ver":"1.0","iss":"https://[mydomain].b2clogin.com/b3d62253-3d6e-453e-bb55-26d87c104a42/v2.0/","sub":"00000000-0000-0000-cdea-6895a0b0757c","aud":"dc3eeaae-c30a-4312-b346-b0919f7d4da1","acr":"b2c_1a_signup_signin","nonce":"defaultNonce","iat":1588317833,"auth_time":1588317833,"tid":"9188040d-6c67-4c5b-b112-36a304b66dad","name":"[my name]","idp":"https://login.microsoftonline.com/[my guid]/v2.0"}'.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: 2020-05-01 17:24:14,518 INFO Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler - JwtB2CBearer was not authenticated. Failure message: IDX10501: Signature validation failed. Unable to match key:
kid: 'TwMsJMU3i_L7zOBKeOw7nWYHov3-eY70IsPfs1gT3aU'.
Exceptions caught:
'System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.
Algorithm: 'HS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk', InternalId: 'e4c64ce7-9e7d-40e2-936a-6658d8f92f07'.'
is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
'.
The Policy Key in Identity Experience Framework is setup using "Manual" configuration and the secret key is something I generated within the Azure AD B2C App.

I regenerated my tokens as guided here: https://aka.ms/ief
Ensured my cryptographic key from original post was set to:
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer"/>
</CryptographicKeys>
I also ensured my JwtIssuer within TrustFrameworkBase.xml has its keys set to:
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
<Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" />
</CryptographicKeys>
It still wouldn't validate, I would get this error:
IDX10501: Signature validation failed. Unable to match keys
Through the guidance of this post: IDX10501: Signature validation failed. Unable to match keys I managed to get it working by setting the IssuerSigningKeys when configuring the JwtBearerOptions:
internal class JwtBearerOptionsConfiguration : IConfigureNamedOptions<JwtBearerOptions>
{
private readonly AzureAdB2COptions b2cOptions;
private readonly IMetaDataFromClaimsPrincipalService metaDataFromClaimsService;
public JwtBearerOptionsConfiguration(IOptions<AzureAdB2COptions> b2cOptions, IMetaDataFromClaimsPrincipalService metaDataFromClaims)
{
this.b2cOptions = b2cOptions.Value;
this.metaDataFromClaimsService = metaDataFromClaims;
}
public void Configure(JwtBearerOptions options)
{
this.Configure(Options.DefaultName, options);
}
public void Configure(string name, JwtBearerOptions options)
{
AzureAdB2COptions currentOptions = this.b2cOptions;
options.Audience = currentOptions.ClientId;
options.Authority = currentOptions.BuildAuthority(currentOptions.SignUpSignInPolicyIds.Last());
options.IncludeErrorDetails = true;
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{options.Authority}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
var openidconfig = configManager.GetConfigurationAsync().Result;
options.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeys = openidconfig.SigningKeys,
ValidateIssuerSigningKey = true
};
options.Events = new JwtBearerEvents
{
OnTokenValidated = this.OnTokenValidated,
OnAuthenticationFailed = this.OnAuthenticationFailed
};
}
private Task OnAuthenticationFailed(AuthenticationFailedContext arg)
{
return Task.CompletedTask;
}
private Task OnTokenValidated(TokenValidatedContext arg)
{
// Check if the authenticated principal has a valid Trust Framework Policy, otherwise do not grant access
string tfp = this.metaDataFromClaimsService.GetTrustFrameworkPolicy(arg.Principal);
if (!this.b2cOptions.SignUpSignInPolicyIds.Contains(tfp))
arg.Fail("Could not validate the Trust Framework Policy");
return Task.CompletedTask;
}
}
Thanks #Jas Suri

Related

AADB2C Claims Transformation for JSON from Federated Identity

I am writing IEF policy integrating with Federated Identity Provider. IDP returns claims in id_token as JSON. when I use claims mapping custom_attributes in output claims, I am getting AAD Exception as
An unexpected type "System.Collections.Generic.List1[System.Collections.Generic.KeyValuePair2[System.String,System.Object]]" was encountered of the claim with claim type id "custom_attributes"
here is my claim mapping:
<OutputClaim ClaimTypeReferenceId="custom_attributes" PartnerClaimType="custom_attributes"/>
claim Schema as:
<ClaimType Id="custom_attributes">
<DisplayName>custom_attributes</DisplayName>
<DataType>string</DataType>
<UserHelpText>Add help text here</UserHelpText>
</ClaimType>
id_token looks like below:
{
"custom_attributes":{
"emailAddress": "someone#example.com",
"displayName": "Someone",
"id" : 6353399
}
}
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="GetRequestorIdClaimFromJsonClaimsTransformation" />
</OutputClaimsTransformations>
claim Schema as
<ClaimsTransformation Id="GetRequestorIdClaimFromJsonClaimsTransformation" TransformationMethod="GetClaimFromJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="custom_attributes" TransformationClaimType="inputJson" />
</InputClaims>
<InputParameters>
<InputParameter Id="claimToExtract" DataType="string" Value="id"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="requestorid" TransformationClaimType="extractedClaim" />
</OutputClaims>
</ClaimsTransformation>
I found a solution to the problem and added it here
https://stackoverflow.com/questions/68086538/why-i-am-gettting-error-in-outputclaim-for-json-object-key-value-pair

Optional Resources in Spring.Net

How to include Spring configuration files optionally? I think about something simular to this:
<spring>
<context>
<resource uri="file:///Objects/RequiredObjects.xml" />
<resource uri="file:///Objects/OptionalObjects.xml" required="false" />
</context>
This way I could provide developers the possibility to override some configuration parts (e.g. for a local speed improvement or automatism during app startup) without affecting the app.config and the problem that a developer could checkin his modified file when it is not really his intent to change the config for all.
Not as simple as in AutoFac (because there is already a builtin way) but possible to achieve something similar with a little coding:
using System.IO;
using System.Xml;
using Spring.Core.IO;
public class OptionalFileSystemResource : FileSystemResource
{
public OptionalFileSystemResource(string uri)
: base(uri)
{
}
public override Stream InputStream
{
get
{
if (System.IO.File.Exists(this.Uri.LocalPath))
{
return base.InputStream;
}
return CreateEmptyStream();
}
}
private static Stream CreateEmptyStream()
{
var xml = new XmlDocument();
xml.LoadXml("<objects />");
var stream = new MemoryStream();
xml.Save(stream);
stream.Position = 0;
return stream;
}
}
Register a section handler:
<sectionGroup name="spring">
...
<section name="resourceHandlers" type="Spring.Context.Support.ResourceHandlersSectionHandler, Spring.Core"/>
...
</sectionGroup>
...
<spring>
<resourceHandlers>
<handler protocol="optionalfile" type="MyCoolStuff.OptionalFileSystemResource, MyCoolStuff" />
</resourceHandlers>
...
<context>
<resource uri="file://Config/MyMandatoryFile.xml" />
<resource uri="optionalfile://Config/MyOptionalFile.xml" />
...
You'll find more information about resources and resource handlers in the Spring.Net documentation.

Spring Security 3.1: Active Directory Authentication and local DB Authorization

I am using Spring Security 3.1 for Active Directory authentication and a local db for loading the authorities. I have seen similar examples but it is still not clear for me what exactly I should use. My current settings in spring-security.xml is:
<!-- LDAP server details -->
<security:authentication-manager>
<security:authentication-provider ref="ldapActiveDirectoryAuthProvider" />
</security:authentication-manager>
<beans:bean id="ldapActiveDirectoryAuthProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<beans:constructor-arg value="${ldap.domain}" />
<beans:constructor-arg value="${ldap.url}" />
<beans:property name="useAuthenticationRequestCredentials" value="true" />
<beans:property name="convertSubErrorCodesToExceptions" value="true" />
</beans:bean>
I have a class let's call it: "BookStoreDbAuthPopulator.java". Inside this class, I am calling this method:
// Load additional authorities and create an Authentication object
final List<GrantedAuthority> authorities = loadRolesFromDatabaseHere();
What is not still clear for me: Which interface should "BookStoreDbAuthPopulator.java" implements in order to add the loaded authorities from db to the UserDetails? "UserDetailsContextMapper" or "GrantedAuthoritiesMapper" or "AuthenticationProvider"?
Based on this solution: Spring Security 3 Active Directory Authentication, Database Authorization
"BookStoreDbAuthPopulator.java" should implement "AuthenticationProvider". My doubt is if I should use "BookStoreDbAuthPopulator.java" as a property for "ldapActiveDirectoryAuthProvider" bean?
Many thanks in advance.
My final solution is "BookStoreDbAuthPopulator.java" implements "UserDetailsContextMapper".
public class BookStoreDbAuthPopulator implements UserDetailsContextMapper {
// populating roles assigned to the user from AUTHORITIES table in DB
private List<SimpleGrantedAuthority> loadRolesFromDatabase(String username) {
//"SELECT ROLE FROM AUTHORITIES WHERE LCASE(USERNAME) LIKE ?"
...
}
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
List<SimpleGrantedAuthority> allAuthorities = new ArrayList<SimpleGrantedAuthority>();
for (GrantedAuthority auth : authorities) {
if (auth != null && !auth.getAuthority().isEmpty()) {
allAuthorities.add((SimpleGrantedAuthority) auth);
}
}
// add additional roles from the database table
allAuthorities.addAll(loadRolesFromDatabase(username));
return new User(username, "", true, true, true, true, allAuthorities);
}
#Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
}
Then in spring-security.xml
<!-- AuthenticationManager: AuthenticationProvider, LDAP server details -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="ldapActiveDirectoryAuthProvider" />
</security:authentication-manager>
<beans:bean id="ldapActiveDirectoryAuthProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<!-- the domain name (may be null or empty). If no domain name is configured, it is assumed that the username will always contain the domain name. -->
<beans:constructor-arg value="${ldap.domain}" />
<!-- an LDAP url (or multiple URLs) -->
<beans:constructor-arg value="${ldap.url}" />
<!-- Determines whether the supplied password will be used as the credentials in the successful authentication token. -->
<beans:property name="useAuthenticationRequestCredentials" value="true" />
<!-- by setting this property to true, when the authentication fails the error codes will also be used to control the exception raised. -->
<beans:property name="convertSubErrorCodesToExceptions" value="true" />
<!-- for customizing user authorities -->
<beans:property name="userDetailsContextMapper" ref="myUserDetailsContextMapper" />
</beans:bean>
<!-- Customizing UserDetail -->
<beans:bean id="myUserDetailsContextMapper" class="com.mybookstore.mywebcomp.w.BookStoreDbAuthPopulator">
</beans:bean>

NullReferenceException on Bootstraptoken (ACS authentication)

I've been following the steps to make a Windows 8 Store app get an ACS token as described here:
Does the WebAuthenticationBroker work in Windows 8 Metro App post Release Candidate
Authentication method of Windows 8 client
private async void Authenticate()
{
WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
new Uri("https://myACSnamespace.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http://localhost:12714/"),
new Uri("http://mypublicIPaddress:80/WebAppMVCAPI/api/federation/end"));
My controller on the web application is programmed as follows:
public class FederationController : ApiController
{
protected virtual string ExtractBootstrapToken()
{
return HttpContext.Current.User.BootstrapToken();
}
[HttpGet]
public string Get()
{
return "Hello Get World";
}
[HttpPost]
public HttpResponseMessage Post()
{
var response = this.Request.CreateResponse(HttpStatusCode.Redirect);
response.Headers.Add("Location", "/WebAppMVCAPI/api/federation/end?acsToken=" + ExtractBootstrapToken());
return response;
}
}
}
The idea is to have the Windows 8 store app get a token from ACS with a Facebook login. When I launch the win8 client, the application shows a Facebook login page. However, the instruction return HttpContext.Current.User.Bootstraptoken() fails with the following exception:
NullReferenceException. Object reference not set to an instance of an object.
My web.config looks like this:
<microsoft.identityModel>
<service saveBootstrapTokens="true">
<audienceUris>
<add value="http://localhost:80" />
</audienceUris>
<federatedAuthentication>
<wsFederation passiveRedirectEnabled="true" issuer="https://bondsapp.accesscontrol.windows.net/v2/wsfederation" realm="http://localhost:80/" reply="http://localhost:80/" requireHttps="false" />
<cookieHandler requireSsl="false" path="/" />
</federatedAuthentication>
<issuerNameRegistry type="Microsoft.IdentityModel.Swt.SwtIssuerNameRegistry, Wif.Swt">
<trustedIssuers>
<add name="https://bondsapp.accesscontrol.windows.net/" thumbprint="xxxxx" />
</trustedIssuers>
</issuerNameRegistry>
<securityTokenHandlers>
<add type="Microsoft.IdentityModel.Swt.SwtSecurityTokenHandler, Wif.Swt" />
</securityTokenHandlers>
<issuerTokenResolver type="Microsoft.IdentityModel.Swt.SwtIssuerTokenResolver, Wif.Swt" />
</service>
Can somebody shed some light on how to use the Bootstraptoken method to get an ACS token?
Thanks
Luis
I don't believe that federated authentication sets HttpContext.User by default. Try
(Thread.CurrentPrincipal as IClaimsPrincipal).Identities[0].BootstrapToken
Assuming that you've gone through the token handler pipeline (WS-FAM) at your site, this should be populated. This will be a SecurityToken object, which you can then serialize using the proper SecurityTokenHandler class.
Did you try this :
BootstrapContext bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext;
SecurityToken st = bootstrapContext.SecurityToken;
Take a look on Vittorio's post:
http://blogs.msdn.com/b/vbertocci/archive/2012/11/30/using-the-bootstrapcontext-property-in-net-4-5.aspx

Accessing Authentication variable from LogoutHandler or LogoutFilter in Spring security

In one of my project I have configured Spring Security to handle user authentication.
My config file looks like this:
<http use-expressions="true">
<intercept-url pattern="/" access="permitAll()" />
<intercept-url pattern="/**" access="isAuthenticated()" />
<form-login default-target-url="/main" login-page="/" always-use-default-target="true" username-parameter="userId" password-parameter="password" />
<custom-filter ref="customLogoutFilter" position="LOGOUT_FILTER"/-->
<session-management invalid-session-url="/" session-authentication-strategy-ref="sas" />
</http>
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
<beans:bean id="customLogoutHandler" class="com.somepack.CustomLogoutHandler"/>
<beans:bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<beans:constructor-arg index="0" ref="customLogoutHandler"/>
<beans:constructor-arg index="1" ref="customLogoutFilter"/>
<beans:property name="filterProcessesUrl" value="/"/>
</beans:bean>
<beans:bean id="customLogoutFilter" class="com.somepack.CustomLogoutFilter">
<beans:property name="reportDir" value="/tmp/reports"/>
</beans:bean>
My CustomLogoutFilter class looks like
public class CustomLogoutFilter implements LogoutHandler {
private String reportDir;
public String getReportDir() {
return reportDir;
}
public void setReportDir(String reportDir) {
this.reportDir = reportDir;
}
#Override
public void logout(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) {
String userName = authentication.getName();
File folder = new File(reportDir, userName);
deleteDir(folder); //delete function to delete Logged User specific directory
logService.info("Logout", userName, EventCode.LOGOUT,
String.format("User %s logged out successfully", userName));
for (Cookie cookie : request.getCookies()) {
printcookies(cookie);
if (cookie.equals("JSESSIONID")) {
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
request.getSession().invalidate();
}
}
But this piece of code is not working as the filter is getting called at the very first request for the Login page (even it may would get called in every request) and I am getting an NullPointerException in the
String userName = authentication.getName() line.
In fact instead of Using LogoutFilter if I use Logouthandler, I get the same error:
My handler looks like this:
public class CustomLogoutHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler{
private String reportDir;
public String getReportDir() {
return reportDir;
}
public void setReportDir(String reportDir) {
this.reportDir = reportDir;
}
#Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException,
ServletException {
String userName = authentication.getName();
File folder = new File(reportDir, userName);
deleteDir(folder);
logService.info("Logout", userName, EventCode.LOGOUT, String.format("User %s logged out successfully", userName));
super.handle(request, response, authentication);
}
and config file changed to:
<http use-expressions="true">
<intercept-url pattern="/" access="permitAll()" />
<intercept-url pattern="/**" access="isAuthenticated()" />
<form-login default-target-url="/main" login-page="/" always-use-default-target="true" username-parameter="userId" password-parameter="password" />
<logout delete-cookies="JSESSIONID" invalidate-session="true" success-handler-ref="customLogoutHandler" logout-url="/logout" />
<session-management invalid-session-url="/" session-authentication-strategy-ref="sas" />
</http>
<beans:bean id="customLogoutHandler" class="sequent.ui.security.CustomLogoutHandler">
<beans:property name="reportDir" value="/tmp/reports" />
</beans:bean>
Not sure how can I resolve this issue.
Please help.
In short my basic requirement is that, I need to access the User Principal in the Logout mechanism which triggered when either User clicks on the Logout button or the session expires. I need the User information because the application creates temporary folder in the name of logged user which I need to delete at the time when he log off.
Appreciate your help please!!
-Raul
You have set the filerProcessesUrl of the LogoutFilter to "/" which means that every time a user browses to the domain root, the filter will attempt to logout the user. Use a specific logout URL (or the default value) and check whether the user is actually authenticated before you try to do a logout (make sure the Authentication instance isn't null).
If you need to deal with session timeouts, where the user fails to logout, then you will also have to implement an HttpSessionListener which identifies the user from the session and performs whatever clean-up you need. This would be added to your web.xml file. Note that this class isn't invoked during a user request, so you can't use the SecurityContext to obtain information about the user, you must get it from the session object which is passed to the listener before the session is invalidated.