Custom tokenhandler in MobileAppService, hosted at azure, not handling all the messaged - asp.net

I have implemented my own JWT TokenHandler for the MobileAppService backend (ASP .Net, MVC).
The app service in ConfigureMobileApp is configured to use my custom TokenHandler instead of the AppServiceTokenHandler, like this:
public static void ConfigureMobileApp(IAppBuilder app)
{
....
HttpConfiguration httpConfig = new HttpConfiguration();
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = new TokenHandler() //httpConfig.GetAppServiceTokenHandler()
});
....
}
The custom TokenHandler is invoked, the JWT token is processed and the user is authorized. Everything is working perfectly fine. However, when I use the MobileServiceClient of my app to send messages, I see this exception being caught in the backend server log:
Microsoft.Azure.AppService.Authentication Warning: 0 : JWT validation failed: IDX10503: Signature validation failed. Keys tried: 'System.IdentityModel.Tokens.InMemorySymmetricSecurityKey
'.
Exceptions caught:
Since the exception is being caught, the application works fine. So what is happening is that the token is being sent simultaneously to both my custom TokenHandler and the default AppServiceTokenHandler. Since the JWT token has some claims which are not recognized by the default AppServiceTokenHandler, it throws (and catches) an exception.
Is there anyway I can force the AppServiceTokenHandler (or maybe other token handler which is there by default and I am not aware of) to be disabled?
Update:
Following Amor - MSFT's suggestion, I changed the code to what he proposed, setting the TokenHandler as
httpConfig.SetAppServiceTokenHandler(new TokenHandler());
but it didn't help. I also edited the web.config as follows, but it had no effect:
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<remove type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add type="MyApp.ASP.Handlers.TokenHandler, MyApp.ASP, Version=1.0.0.0, Culture=neutral" />
</securityTokenHandlers>
</identityConfiguration>
Also tried the following (as suggested here), still no effect:
<securityTokenHandlers>
<clear />
</securityTokenHandlers>

Is there anyway I can force the AppServiceTokenHandler (or maybe other token handler which is there by default and I am not aware of) to be disabled?
Please save your custom token handler to config.Properties before using it. We can invoke SetAppServiceTokenHandler method to do it.
var tokenHandler = new CustomTokenHandler();
httpConfig.SetAppServiceTokenHandler(tokenHandler);
Based on the document of AuthenticationHttpConfigurationExtensions.cs, SetAppServiceTokenHandler method will help us save the custom token handle to config.Properties and it will return the custom handle when the config.GetAppServiceTokenHandler method is invoked. Your final code could be like this,
HttpConfiguration httpConfig = new HttpConfiguration();
var tokenHandler = new CustomTokenHandler();
httpConfig.SetAppServiceTokenHandler(tokenHandler);
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
// This middleware is intended to be used locally for debugging. By default, HostName will
// only have a value when running in an App Service application.
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = httpConfig.GetAppServiceTokenHandler()
});
Edit 5/22/2017
Since you are using AppServiceAuthenticationMiddleware, please make sure you disabled the Authentication / Authorization in Azure portal.

Related

The HTTP request is unauthorized with client authentication scheme 'Anonymous' Dynamics NAV

I have an issue regarding an .net core app (3.1) trying to consume a WS (SOAP) from a Dynamics Nav Server. (on-premise).
When I'm in debugging mode everything works fine, but when I deployed the app to local IIS server I keep getting
"The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'oRswGaADCgEAoxIEEAEA=,Negotiate'."
Client initialization
private async Task<TXSiteUser_PortClient> GetInstanceAsync()
{
EndpointAddress endpointAddress = new EndpointAddress(serviceUrl);
BasicHttpBinding basicHttpBinding = new BasicHttpBinding()
{
MaxReceivedMessageSize = int.MaxValue,
MaxBufferSize = int.MaxValue,
OpenTimeout = TimeSpan.MaxValue,
CloseTimeout = TimeSpan.MaxValue,
ReceiveTimeout = TimeSpan.MaxValue,
SendTimeout = TimeSpan.MaxValue
};
return await Task.Run(() => new TXSiteUser_PortClient(basicHttpBinding, endpointAddress));
}
Example of call to a WS
var client = await GetInstanceAsync();
var user = await client.ReadAsync(new Read { Id = id });
await client.CloseAsync();
return user.TXSiteUser;
The Dynamics server has the credential type "Windows".
And below is the web.config
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath=".\ArtoilPortal.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
The weird thing is that when I'm in debugging mode and testing the endpoints from Swagger, everything works ok. Also, everytime I try to use postman I get a 401, no matter of Auth type (Basic,NTML).
Also i've tried adding
basicHttpBinding.Security.Mode = BasicHttpSecurityMode.Transport;
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
var username = _appSettings.NAVCredentials.UserName;
var password = _appSettings.NAVCredentials.Password;
and extended the PortClient method like this
public TXSiteUser_PortClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress, string userName, string password) :
base(binding, remoteAddress)
{
this.ChannelFactory.Credentials.UserName.UserName = userName;
this.ChannelFactory.Credentials.UserName.Password = password;
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
with no luck
managed to solve the errors.
Firstly, the client initialization code was fixed by using "TransportCredentialOnly" instead of "Transport"
basicHttpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
TransportCredentialOnly mode is used when there's no SSL configured for the SOAP services on Dynamics NAV server.
And the version of "PortClient" method that works for me looks like this:
public TXSiteUser_PortClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress, string userName, string password) :
base(binding, remoteAddress)
{
this.ClientCredentials.Windows.ClientCredential.UserName = userName;
this.ClientCredentials.Windows.ClientCredential.Password = password;
this.ClientCredentials.Windows.ClientCredential.Domain = "";
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
In the original post I was setting
this.ChannelFactory.Credentials.UserName.UserName = userName;
Where it should've been
this.ClientCredentials.Windows.ClientCredential.UserName = userName;
Hopefully this will help somebody at some point

Published Web App Getting AADSTS50011 Error

Trying to fix this Azure Active Directory issue. I have an ASP.Net 4.7 website. It correctly takes me to the SSO page and confirms my identity. However, upon taking me back to my site, I get the message:
AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application: 'MyApplication-ClientID'.
It makes sense that the AD App Registration's | Authentication | Redirect URI does not match what I am sending it. However, as near as I can tell, they do.
Here is my code in the Startup.cs file:
public void Configuration(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId, // MyApplication-ClientID
Authority = authority, // https://login.microsoftonline.com/MyDirectory-TenantID/v2.0
RedirectUri = redirectUri, // https://MySiteName.azurewebsites.net
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters() {
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications {
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
Here are the settings in my Web.config file:
<add key="ClientId" value="MyApplication-ClientID" />
<add key="Tenant" value="common" />
<add key="Authority" value="https://login.microsoftonline.com/MyDirectory-TenantID/v2.0" />
<add key="RedirectUri" value="https://MySiteName.azurewebsites.net" />
Both the Redirect URI and the Configs RedirectUri are: https://MySiteName.azurewebsites.net
Here is the initial request when I am asked to select an account when trying to log in:
https://login.microsoftonline.com/MyDirectory-TenantID/oauth2/v2.0/authorize
?response_type=code+id_token
&redirect_uri=https%3A%2F%2FMySiteName.azurewebsites.net%2F.auth%2Flogin%2Faad%2Fcallback
&client_id=MyApplication-ClientID
&scope=openid+profile+email
&response_mode=form_post
&nonce=3f63a75d79af449082801c5183d5fbdb_20200710145808
&state=redir%3D%252F
So I see the redirect_uri seems to add the /.auth/login/aad/callback to the end of what I told it to be. So I updated the AD App Registration's | Authentication | Redirect URI to match and instead of the above error I get the following error:
The page cannot be displayed because an internal server error has occurred.
I am just at a loss here trying to figure out what is mismatched or perhaps just missing.

Receiving a magic login link on Azure without allowing double escaping

Trying to implement a magic link login on an Asp.net core 2.1 Web app. Works like a charm locally, however when deploying to Azure I get an error message: `The request contained a double escape sequence and request filtering is configured on the Web server to deny double escape sequences
MagicLinkSender.cs
var token = await _userManager.GenerateUserTokenAsync(
user: user,
tokenProvider: "MagicLinkTokenProvider",
purpose: "magic-link"
);
var magiclink = _urlHelper.Link(
routeName: "MagicLinkRoute",
values: new { userid = user.Id, token = token, });
AccountController
[HttpGet("/magic/{userid}/{token}", Name = "MagicLinkRoute")]
public async Task<IActionResult> MagicLogin([FromRoute]string userid, [FromRoute]string token )
{
// Sign the user out if they're signed in
if(_signInManager.IsSignedIn(User))
{
await _signInManager.SignOutAsync();
}
var user = await _signInManager.UserManager.FindByIdAsync(userid);
if(user != null)
{
token = token.Replace("%2F", "/");
var isValid = await _signInManager.UserManager.VerifyUserTokenAsync(
user: user,
tokenProvider: "MagicLinkTokenProvider",
purpose: "magic-link",
token: token
);
if(isValid)
{
await _signInManager.UserManager.UpdateSecurityStampAsync(user);
await _signInManager.SignInAsync(user, isPersistent: true);
}
}
return RedirectToPage("/Profile/Index");
}
Seems like I can get around this with allowing doublescaping in web.config:
<system.webServer>
<security>
<requestFiltering allowDoubleEscaping="true" />
</security>
</system.webServer>
However this seems to open some security holes. Are there better alternatives to get this working on Azure?
Some characters require additional configuration depending on your hosting environment:
To allow '+' in item names in ASP.NET 2.0 and 4.0 set the configuration\in your web.config file.
<system.webServer>
<security>
<requestFiltering allowDoubleEscaping="true" />
</security>
</system.webServer>
To allow '&' and '%' in ASP.NET 4.0 set the configurationin your web.config file.
<system.web>
<httpRuntime requestPathInvalidCharacters=""/>
</system.web>
To allow trailing dots ('.') in ASP.NET 4.0 set configuration in your web.config file.
<system.web>
<httpRuntime relaxedUrlToFileSystemMapping="true"/>
</system.web>
For more details, you could refer to this article and this one.
Changing to use parameters from querystring instead of route seems to solve this problem when deploying to Azure.
[HttpGet("/magic", Name = "MagicLinkRoute")]
public async Task<IActionResult> MagicLogin([FromQuery]string userid, [FromQuery]string token )
{
// ...

Owin / Mvc add support for bearer token authentication

My objective is to have an Asp.Net Mvc action secured with OpenId authentication, and support 2 types of clients: browser and a native WPF application. The STS I will use is ADFS 2016.
Currently clients browsers works well. For this, I have UseOpenIdConnectAuthentication configured in my startup class.
I'm able to call my Mvc action (secured with Authorize attribute), user is redirected to STS, and once authentication is done, I come back to my Mvc action with a ClaimsIdentity properly filled.
Now I'm trying to have a native WPF app able to authenticate to the same Mvc action in the same Web app, and things are getting tricky.
On the client side (my WPF application), I'm using ADAL and the following code:
var authContext = new AuthenticationContext("<MySTSUri>");
var authResult = await authContext.AcquireTokenAsync(
"http://localhost:1276/openid/login",
"MyNativeAppId",
new Uri("myapp://openid"),
new PlatformParameters(PromptBehavior.Auto),
UserIdentifier.AnyUser,
"");
if (!string.IsNullOrEmpty(authResult.AccessToken))
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(authResult.AccessTokenType, authResult.AccessToken);
HttpResponseMessage response = await httpClient.GetAsync("http://localhost:1276/openid/login");
if (response.IsSuccessStatusCode)
{
var text = await response.Content.ReadAsStringAsync();
}
}
}
The problem is basically that I can't tell the Web app to be able to validate this type of ADAL request.
I've tried various things in the Web application Owin startup file configuration:
leaves UseOpenIdConnectAuthentication: it doesn't seem sufficient, I'm redirected to STS with the ClientId of the Web application
UseActiveDirectoryFederationServicesBearerAuthentication api since I know my STS will always be an ADFS
UseOAuthBearerAuthentication
None of them are working.
Please can someone help how to achieve this?
Am I going in the right direction?
Any ideas/pointers would be greatly appreciated.
Thanks,
Alex
I've managed to get it working. I post the answer for the record.
What helped me a lot is to enable Owin logs in the web.config:
<system.diagnostics>
<switches>
<add name="Microsoft.Owin" value="Verbose" />
</switches>
</system.diagnostics>
Then with Owin, you can simply chain multiple authentication methods. So in my case, I've just used:
app.UseActiveDirectoryFederationServicesBearerAuthentication(
new ActiveDirectoryFederationServicesBearerAuthenticationOptions
{
MetadataEndpoint = adfsMetadataEndpoint,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudiences = new[] { validAudience }
}
});
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
{
AuthenticationType = "OpenId",
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
ResponseType = OpenIdConnectResponseTypes.CodeIdToken,
Scope = "openid",
SignInAsAuthenticationType = "Cookies"
});
Cheers,
Alex

How to resolve WCF Rest MethodNotFound (405) Error when appting to PUT via WCF Rest Starter Kit HttpClient

I have looked at nearly every single WCF Rest PUT/POST issues on SO here and have still been unable to determine why I am unable to PUT or POST to my web service but am able to call a test GetTime method via GET.
This particular service exchanges custom credential information (username, password and some other information) for a token. This token information is encrypted and added to the header of subsequent requests to other web services so that username/passwords don't have to be passed around everywhere.
All web service calls are still treated as stateless, but require this auth header information rather username/passwords to access secured service operations.
Contract
[ServiceContract(Namespace = "")]
public interface IUserService
{
[WebInvoke(Method = "PUT", UriTemplate = "users/{username}/session")]
[WebHelp(Comment = "Creates a new session for the specified username")]
[OperationContract]
AuthToken PutSession(string username, CustomCredential credential);
...
[WebInvoke(Method = "GET", UriTemplate = "time")]
[WebHelp(Comment = "Test method; returns the time")]
[RequireAuthToken]
[OperationContract]
DateTime GetTime();
}
Service Impelementation
public class UserService : IUserService
{
#region UserService Members
public AuthToken PutSession(string username, CustomCredential credential)
{
// ...
}
public DateTime GetTime()
{
return DateTime.Now;
}
}
Test Code
[Fact]
public void When_Authenticated_And_Getting_Time_Expect_200_Status()
{
// arrange
using (var client = Get_HttpClient_With_AuthHeaders()) {
// act
var result = client.Get("time");
// assert
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
}
}
The aforementioned GetTime works (just a test method I added).
[Fact]
public void When_PuttingSession_Expect_AuthToken_Is_Returned()
{
// arrange
using (var client = Get_HttpClient_With_No_Auth_Headers()) {
var cred = new CustomCredential("test", "password", 1);
var content = HttpContentExtensions.CreateDataContract<CustomCredential>(cred);
// act
HttpResponseMessage response = client.Put("users/test/session", content);
// assert
response.EnsureStatusIsSuccessful();
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
var authToken = response.Content.ReadAsDataContract<AuthToken>();
Assert.NotNull(authToken);
}
}
The above put returns a MethodNotAllowed (405).
Just as another test I added a Mex endpoint to the service, created a new console app and added a 'service reference' to this service. Using the generated client proxy code, I was able to something similar to....
IUserServiceClient client = new IUserServiceClient();
CustomCredential cred = ...
AuthToken token = client.PutSession("test_username", cred);
What this suggests is that...
The web service is hosted in IIS correctly
I am able to consume the service using SOAP client proxy generated code
I am able to consume GET requests via more friendly HttpClient / rest toolkit
I have tested this get in a browser and it works
For some reason put and other related methods (post, delete. etc) do not work via rest toolkit
Have no idea what is causing this one.
EDIT
I also noticed that IIS must have created this web.config in the root of the website where the services are hosted which would seem to be allowing the main HTTP verbs.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
<requestFiltering>
<verbs>
<add verb="PUT" allowed="true" />
<add verb="GET" allowed="true" />
<add verb="POST" allowed="true" />
<add verb="DELETE" allowed="true" />
</verbs>
<fileExtensions>
<add fileExtension=".svc" allowed="true" />
</fileExtensions>
</requestFiltering>
</security>
</system.webServer>
</configuration>
I have also checked in IIS 7 the handler mappings for *.svc and checked that 'all verbs' are enabled.
Check that related handler for .svc in IIS is allowed to process HTTP POST, PUT, DELETE. In case of problems with only PUT and DELETE check that your virtual directory is not configured for WebDAV (I think it is some HTTP module).

Resources