Cognito refresh token with secret key causes unable to verify secret hash - c#

I am using below code to refresh token in an AWS Cognito application configured with secret key. No matter which configuration I have tried it always causes common issue of unable to verify secret hash. The input parameters have been trippled checked and the login functionality works well. Can anyone advice on what is the issue with the below codeset.
public static async Task<AuthFlowResponse> GetRefreshAsync(string userName, string refreshToken)
{
AmazonCognitoIdentityProviderClient provider = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials(), FallbackRegionFactory.GetRegionEndpoint());
CognitoUserPool userPool = new CognitoUserPool(userPoolId, clientId, provider, clientSecret);
CognitoUser user = new CognitoUser(userName, clientId, userPool, provider, clientSecret);
user.SessionTokens = new CognitoUserSession(null, null, refreshToken, DateTime.Now, DateTime.Now.AddHours(1));
InitiateRefreshTokenAuthRequest refreshRequest = new InitiateRefreshTokenAuthRequest()
{
AuthFlowType = AuthFlowType.REFRESH_TOKEN_AUTH
};
return await user.StartWithRefreshTokenAuthAsync(refreshRequest).ConfigureAwait(false);
}

Related

Get Token from Azure using AAD App (ClientID, TenantID, Cert-Thumbprint)

I have below Method to get a token from Azure using ClientID, TenantID and AADAppPassword
This is working awesome but now I need to switch to different AAD AppID and use Certificate Thumbprint Or Certificate pfx.
I don't want to change my 1000+ lines of code.
Can someone help me get a token the same way I'm getting using below Method but use Certificate Thumbprint instead and which returns token so that I can call the method right before I'm about to make rest API call.
public static async Task<string> GetAccessToken(string tenantId, string clientId, string clientKey)
{
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId, clientKey);
var result = await authenticationContext
.AcquireTokenAsync("https://management.azure.com/", credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
You must use ClientAssertionCertificate instead of ClientCredential
X509Certificate2 cert = ReadCertificateFromStore(config.CertName);
certCred = new ClientAssertionCertificate(config.ClientId, cert);
result = await authContext.AcquireTokenAsync(todoListResourceId, certCred);
You may refer the Azure AD v1 Sample for this.
MSAL.NET is now the recommended auth library to use with the Microsoft identity platform. No new features will be implemented on ADAL.NET. The efforts are focused on improving MSAL. You can refer the documentation here if you are planning to migrate applications to MSAL.NET

retrieve secret from azure key vault

I am not able to retrieve a secret from azure key vault to a .net console app which runs in azure windows VM. Below is the code i have used and i have given service principal all permission in key vault.
var kvc = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(
async (string authority, string resource, string scope) => {
var authContext = new AuthenticationContext(authority);
var credential = new ClientCredential("App id, "secret identifier uri");
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, credential);
if (result == null) {
throw new InvalidOperationException("Failed to retrieve JWT token");
}
return result.AccessToken;
}
));
Please reference this tutorial in the Microsoft documentation, where you can find the correct way to use Azure Key Vault inside a Windows VM, and using .NET. Note: In this solution, you will use Managed Service Identity, instead of the traditional Service Principal.

AcquireToken(string resource, string clientId, UserCredential userCredential) not working for Azure Authentication

Using Microsoft.IdentityModel.Clients.ActiveDirectory(2.22.302111727) I am trying to get the access token and the refresh token via ADAL AcquireToken(resourceUri, new ClientCredential(clientId,clientSecret) but I am able to get the only access token only but when I am using the same app with AcquireToken(resourceUri, clientId, userCredential) and passing the username and password as the userCredentials then I am able to get both the access as well as refresh token.
string resourceUri = "https://graph.microsoft.com";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";
AuthenticationResult token = authContext.AcquireToken(resourceUri, new
ClientCredential(clientId,clientSecret));
string resourceUri = "https://graph.microsoft.com";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";
AuthenticationResult token = authContext.AcquireToken(resourceUri, new
ClientCredential(clientId,clientSecret));
I have also tried using AcquireToken(clientId, resourceUri, new Uri(redirectUri)) but it gives me error "AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'" and I have tried AcquireToken(resourceUri, clientId, new Uri(redirectUri),
PromptBehavior.RefreshSession ,new UserIdentifier("xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx", UserIdentifierType.UniqueId)) and it gives me error "AADSTS650057: Invalid resource. The client has requested access to a resource which is not listed in the requested permissions in the client's application registration. Client app ID: 00000003-0000-0000-c000-000000000000(Microsoft Graph)."
Please explain the exact resource value that is missing here in error AADSTS650057.
You may be hitting several different error. OAuth2 client credentials grant does not return refresh tokens (enter link description here ) as you can get a new token with the existing credentials. Requests involving user credentials do return refresh tokens as you may want to renew the access token without having to prompt the user for credentials.
AADSTS650057 is most likely caused by your application being registered in AAD without having permission to call MS Graph. An application must have either Application Permission (when using Client Credentials) or Delegated Permission (when using user creds) to get a token to this resource.
1.Install this from nuget package
using Microsoft.Identity.Client;
2. add this code
string clientId = "Get it from Admin"
string clientSecret = "Get it from Admin"
string authorityUrl = "Get it from Admin"
string authorityId = "Get it from Admin";
string authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", authorityId);
var app = ConfidentialClientApplicationBuilder.Create(clientId).WithAuthority(authority).WithClientSecret(clientSecret).Build();
var authResult = Task.Run(() => app.AcquireTokenForClient(new[] { $"{apiEndpointUrl}/.default" }).ExecuteAsync()).Result;
return authResult.AccessToken;

Using authentication token in azure sdk fluent

To authenticate with Azure in azure sdk fluent nuget, there is a method that uses client id and secret as below
var azureCredentials = new AzureCredentials(new
ServicePrincipalLoginInformation
{
ClientId = "ClientId",
ClientSecret = "ClientSecret"
}, "tenantId", AzureEnvironment.AzureGlobalCloud);
Is there any interface where authentication token (JWT) can be used instead of using client id and secret while creating IAzure in the below code?
_azure = Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(azureCredentials)
.WithSubscription(_subscriptionId);
Note: I have a separate authenticater module that keeps client id and secret with itself and uses them to get authentication token which will be used by other components/sdks.
The answer is actually yes, you can use the authentication token (JWT). It's just not that obvious.
var context = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId, false);
var result = context.AcquireToken("https://management.core.windows.net/", clientId, new Uri("http://localhost"), PromptBehavior.Always);
var token = result.AccessToken;
var client = RestClient
.Configure()
.WithEnvironment(AzureEnvironment.AzureGlobalCloud)
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.WithCredentials(new TokenCredentials(token))
.Build();
var azure = Azure
.Authenticate(client, tenantId)
.WithSubscription(subscriptionId);
Sigh...they've changed the WithCredentials to use an AzureCredentials instead of a ServiceClientCredentials. Here's an updated version:-
var context = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId, false);
var result = context.AcquireToken("https://management.core.windows.net/", clientId, new Uri("http://localhost"), PromptBehavior.Always);
var token = result.AccessToken;
var tokenCredentials = new TokenCredentials(token);
var azureCredentials = new AzureCredentials(
tokenCredentials,
tokenCredentials,
tenantId,
AzureEnvironment.AzureGlobalCloud);
var client = RestClient
.Configure()
.WithEnvironment(AzureEnvironment.AzureGlobalCloud)
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.WithCredentials(azureCredentials)
.Build();
var azure = Azure
.Authenticate(client, tenantId)
.WithSubscription(subscriptionId);
Starting from Azure Management Fluent SDK v1.10 you can use any credentials provider that is derived from ServiceClientCredentials. In other words you should be able to pass already acquired Bearer token string to AzureCredentials constructor like this
var customTokenProvider = new AzureCredentials(
new TokenCredentials(armAuthToken),
new TokenCredentials(graphAuthToken),
tenantId,
AzureEnvironment.AzureGlobalCloud);
var client = RestClient
.Configure()
.WithEnvironment(AzureEnvironment.AzureGlobalCloud)
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.WithCredentials(customTokenProvider)
.Build();
var authenticatedClient = Azure.Authenticate(client, tenantId);
Is there any interface where authentication token (JWT) can be used instead of using client id and secret while creating IAzure in the below code?
In short no. Base on my experence, if we want to access the corresponding Azure resources then we need to get the authentication token (JWT) from the corresponding resources. Azure has lots of resources such as AzureSQL, KeyVault, ResourceManagement etc.
On my option, it is not make senses that use the authentication token (JWT) that can access all of the Azure Resources.
Currently, we could get AzureCredentials from file, ServicePrincipalLoginInformation, UserLoginInformation
If we want to operate a certain resource, then we could use authentication token (JWT) to do, take KeyVault as example.
public static async Task<string> GetAccessToken(string azureTenantId,string azureAppId,string azureSecretKey)
{
var context = new AuthenticationContext("https://login.windows.net/" + azureTenantId);
ClientCredential clientCredential = new ClientCredential(azureAppId, azureSecretKey);
var tokenResponse =await context.AcquireTokenAsync("https://vault.azure.net", clientCredential); //KeyVault resource : https://vault.azure.net
var accessToken = tokenResponse.AccessToken;
return accessToken;
}
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetAccessToken));

AzureB2C Identity Provider Login Failed : AADB2C99002: User does not exist

Im trying Azure AD B2C and I've added Google and Microsoft Identity Providers through Azure Portal.
When i try to login with Microsoft OR Google IP, i always receive following error message in the OnAuthenticationFailed-Handler:
AADB2C99002: User does not exist. Please sign up before you can sign in.
But when i'm using the "Local Account SignIn" Provided by Azure B2C everything is working fine. Do i missing something in my configuration ?
The following code snippet shows my OWIN Configuration.
private void ConfigureAuthentication(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
OpenIdConnectAuthenticationOptions options = new OpenIdConnectAuthenticationOptions
{
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
SecurityTokenValidated = context => {
return null;
}
},
Scope = "openid offline_access",
// The PolicyConfigurationManager takes care of getting the correct Azure AD authentication
// endpoints from the OpenID Connect metadata endpoint. It is included in the PolicyAuthHelpers folder.
ConfigurationManager = new PolicyConfigurationManager(
String.Format(CultureInfo.InvariantCulture, aadInstance, tenant, "/v2.0", OIDCMetadataSuffix),
new string[] { SignUpPolicyId, SignInPolicyId, ProfilePolicyId }),
// This piece is optional - it is used for displaying the user's name in the navigation bar.
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name",
},
};
app.UseOpenIdConnectAuthentication(options);
}
// This notification can be used to manipulate the OIDC request before it is sent. Here we use it to send the correct policy.
private async Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
PolicyConfigurationManager mgr = notification.Options.ConfigurationManager as PolicyConfigurationManager;
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
OpenIdConnectConfiguration config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, notification.OwinContext.Authentication.AuthenticationResponseRevoke.Properties.Dictionary[AzureB2C.PolicyKey]);
notification.ProtocolMessage.IssuerAddress = config.EndSessionEndpoint;
}
else
{
OpenIdConnectConfiguration config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, notification.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.Dictionary[AzureB2C.PolicyKey]);
notification.ProtocolMessage.IssuerAddress = config.AuthorizationEndpoint;
}
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
// The user's objectId is extracted from the claims provided in the id_token, and used to cache tokens in ADAL
// The authority is constructed by appending your B2C directory's name to "https://login.microsoftonline.com/"
// The client credential is where you provide your application secret, and is used to authenticate the application to Azure AD
string userObjectID = notification.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant, string.Empty, string.Empty);
ClientCredential credential = new ClientCredential(clientId, clientSecret);
// We don't care which policy is used to access the TaskService, so let's use the most recent policy
string mostRecentPolicy = notification.AuthenticationTicket.Identity.FindFirst(AzureB2C.AcrClaimType).Value;
// The Authentication Context is ADAL's primary class, which represents your connection to your B2C directory
// ADAL uses an in-memory token cache by default. In this case, we've extended the default cache to use a simple per-user session cache
AuthenticationContext authContext = new AuthenticationContext(authority, new NaiveSessionCache(userObjectID));
// Here you ask for a token using the web app's clientId as the scope, since the web app and service share the same clientId.
// The token will be stored in the ADAL token cache, for use in our controllers
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(notification.Code, new Uri(redirectUri), credential, new string[] { clientId }, mostRecentPolicy);
}
// Used for avoiding yellow-screen-of-death
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
_log.Error("AuthenticationFailed!\r\nError={0}\r\nErrorDescription={1}\r\n{0}",
notification.ProtocolMessage.Error,
notification.ProtocolMessage.ErrorDescription,
notification.Exception.ToString());
notification.HandleResponse();
notification.Response.Redirect("/Home/OpenIdError?message=" + notification.ProtocolMessage.ErrorDescription);
return Task.FromResult(0);
}
}
External identities first need to 'sign up' as well before signing in. During sign up the external identity is linked to B2C.
In the sign up page you can ask additional attributes for your users, like a customer number. You need this for external identies and for the Local Account users in B2C, no difference between the two.
This is different behaviour compared to adding an identity provider without B2C, where every login just works.
Edit: Like Konstantin mentioned, the new combined sign-up or sign-in policy solves this problem:
https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-reference-policies/#create-a-sign-up-or-sign-in-policy
I was running into the same issue, but was able to circumvent the user "Sign-Up" after user insertion. The issue turned out to be, that to have proper federation occur, the proper values need to be in place.
"identities": [
{
"signInType": "federated",
"issuer": "https://login.microsoftonline.com/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX/v2.0",
"issuerAssignedId": "YYYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"
What was happening was that I was using "issuer": "myDomain.com" which was not resolving correctly to do a login; to which the user then had to "SignUp" via the federated IP.
By changing that from DNS readable name, to the MS login with my AD directories ID (the number provided when switching domain in Azure, XXXX-XXX ...) and the proper issuerAssignedId, from the originating AD issuer, it worked and the user was added.

Categories