High Trust S2S Provider Hosted App with "App + User" Policy - sharepoint

I am relatively new to sharepoint app development.
Trying to create a on premises, High Trust provider hosted app with App + User Policy. I have followed below document to create a demo.
https://msdn.microsoft.com/library/office/fp179901(v=office.15)
http://blogs.msdn.com/b/russmax/archive/2014/06/23/part-1-intro-to-provider-hosted-apps-setup-the-infrastructure.aspx
I am facing few issue and I have some question to clarify, if anybody can help.
1) When I inspect my request in dev tools, it give me below form data.
SPAppToken:
SPSiteUrl:
SPSiteTitle:Home
SPSiteLogoUrl:
SPSiteLanguage:en-US
SPSiteCulture:en-US
SPRedirectMessage:EndpointAuthorityMatches
SPErrorCorrelationId:f069e89c-a0cd-20ce-a1c0-7db95db0334b
now when i inspect log with above corelation id, i am finding below errors.
-- Error when get token for app i:0i.t|ms.sp.ext|ab8ff461-bc75-4516-b475-b666ac47eec0#802f23e1-6e11-45d1-909c-07a7b0ab0ce2,
exception: Microsoft.SharePoint.SPException: The Azure Access Control
service is unavailable.
-- App token requested from appredirect.aspx for site: 92bfe5c4-7255-4b09-a89a-07e0e2b03622 but there was an error in
generating it. This may be a case when we do not need a token or when
the app principal was not properly set up.
-- Getting Error Message for Exception Microsoft.SharePoint.SPException: The Azure Access Control service is
unavailable.
a) I belive in high-trust app it shouldn't look for Azure ACS.
Is this error because of some incorrect configuration?
b) SPAppToken is null here. Is it null always in case of hig trust app?
2) Say I am logged into sharepoint with User A and trying to launch sharepoint app.
Within app code I want to get identity of logged in user(which is A). From below code i found that Request.LogonUserIdentity gives me identity of user A. But how can we sure that request is came from sharepoint only. I can copy the same app URL and paste in browser window and login with window credential and get the same result. So question is how can I verify if its legitimate request came from sharepoint only and no one is faking request.
ALos, when I inspect request in dev tools, its passing Authorization key in request header. What is use of this?
using (var clientContext = TokenHelper.GetS2SClientContextWithWindowsIdentity(hostWeb, Request.LogonUserIdentity)) { clientContext.Load(clientContext.Web, web => web.Title); clientContext.ExecuteQuery(); Response.Write(clientContext.Web.Title); }
3) Also what happens if my app doesnt support windows authentication and only support FBA, is there any way to get user identity in this case?
Any help would be much appreciated.
Thanks

For issue #1: It looks to me that the step # 9 (Configure authentication settings) in this section (from the first MSDN article you have referred) was missed, i.e., 'ACS Control service' was selected instead of 'Use a Certificate' option.
For issue #2: There are helper methods in TokenHelper.cs to validate the AccessToken from the HttpRequest, which identifies the validity of the request.

Related

SaaS App with AzureADB2C: User Flows for Client SSO?

We have a SaaS web app and our clients are requiring SSO authentication for each of them. We are using AzureADB2C and it works great, but now are looking at adding SSO.
I put in the SSO setup into the B2C tenet and it works great, but really messed up our login screen with a "MyCompanySSO" button to log in with, on our customer-facing login screen.
So now my idea is to have a separate user flow that handles each SSO setup. Starting with us. We'd go to MyCompany.OurSaaSApp.us and that'd forward them directly to the user flow endpoint and prompt them to login with their SSO account (AzureAD).
This all seems to try to work, but I'm getting these errors within the AzureADB2C middleware:
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Warning: .AspNetCore.Correlation. state property not found.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Information: Error from RemoteAuthentication: Correlation failed..
Then I get pumped out onto a error page and the login fails.
So 2 things...
1.) Am I going in the right direction knowing what we're wanting to accomplish
2.) What do we need to do to resolve this?
Thanks everyone for the help, it's been greatly appreciated.
(note:)
Just to reiterate. The SSO works properly when the custom identity provider is attached to the existing SignUpOrIn UserFlow I have configured in the app. I'm only getting this error when I try to use another UserFlow that I want to use specifically for this SSO.
I'm not sure about that specific error, although "state" parameter is a parameter that your app sends in the request that will be returned in the token for correlation purposes.
Using and different policy for each federation sounds like the right approach, but if you are doing from a single instance of your app, you'll need to modify the OIDC protocol message with the correct authority (ie policy) on redirect.
In your OIDC middleware configuration, set up a handler for the RedirectToIdentityProvider notification. Then handle it with something like:
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
//var policy = notification.OwinContext.Get<string>("Policy");
var tenantSegment = notification.Request.Path.Value.Split(new char [] { '/'}, StringSplitOptions.RemoveEmptyEntries)[0];
if (!string.IsNullOrEmpty(tenantSegment) && !tenantSegment.Equals(DefaultPolicy))
{
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), $"B2C_1A_{tenantSegment.ToLower()}_SignUpSignInPolicy");
}
return Task.FromResult(0);
}
If you need to inject anything else tenant-related, that would be the place to do it.

Getting an "unauthorized_client" when tryging to get a new access token, why?

I'm developing an API using VS 2017, which I'm testing using Postman. I've been following this blog post Getting started with Windows Azure AD Authentication using Postman. I finally got it to give me an access token and a refresh token. About 3 quarters of the way through the post I came across this:
resource : https://management.core.windows.net
which has to be put into Postman (I'm using the Windows app version of Postman). I wasn't sure what that was for, so I left it off. I eventually got an access token and a refresh token, but when I when to get more info for all subscriptions, I got errors saying that I had specified a valid resource. So, I thought what I'd have to do was start over again, but re-authenticating and getting a code from Azure. I did so, and put that into Postman, as the blog post instructs. However, issuing this POST results in this error:
"error": "unauthorized_client",
"error_description": "AADSTS70002: Error validating credentials. AADSTS65005: 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: d37abf69-42ce-4571-b146-f3422e73f041. Resource value from request: https://management.core.windows.net. Resource app ID: 797f4846-ba00-4fd7-ba43-dac1f8f63013. List of valid resources from app registration: 00000002-0000-0000-c000-000000000000.\r\nTrace ID: 9f028899-6d03-409e-8db4-4e9905000300\r\nCorrelation ID: ec253a1b-9fdc-495f-9310-6b40a42e5d93\r\nTimestamp: 2018-11-19 22:20:02Z",
"error_codes": [
70002,
65005
]
I don't understand why nor what I could have done wrong. I've prespecified the resource as the tutorial instructed, so what have I done wrong?
Looking at the error message, it seems you could have missed adding relevant permissions for "Windows Azure Service Management API" to your app registration.
Steps:
Go to Azure Portal > Azure AD > App registrations > Your app (with app id: d37abf69-42ce-4571-b146-f3422e73f041)
Go to Settings > Required permissions > Add > Select "Windows Azure Service Management API"
Select the relevant permission under Delegated permissions section. Click on select and then "Done". You should get a notification in portal that successfully added permissions. You should see Windows Azure Service Management API listed under required permissions as well.
After these steps, continue as per the blog post you've mentioned with Postman steps to get the access token again.
I found myself in the same situation when i was trying to setup Postman. Its not as complicated as most blogs make seem. I created a tutorial to try to make the process easy.
http://hazelnest.com/blog/blog/2018/11/17/azure-postman-configuration/
https://youtu.be/2bfgeBKRxl4
Hope this helps.

AADSTS70005: response_type 'id_token' is not enabled for the application

AADSTS70005: response_type 'id_token' is not enabled for the
application
I am getting above error even after setting "oauth2AllowImplicitFlow": true, in manifest.
try this:
go to portal.azure.com
select your directory, and go to Azure AD
then select App registration (preview)
select the app you are trying to authenticate (you should already have registered it)
go to the authentication tab
check "ID tokens" in the Advanced Settings section (see the bottom of the attached image)
this have worked for me
I got the error:
AADSTS700054: response_type 'id_token' is not enabled for the application.
And the resolution was setting:
{
"oauth2AllowIdTokenImplicitFlow" : true
}
in Azure Active Directory App Manifest
Make sure you have selected ID tokens (used for implicit and hybrid flows)
You can do from Authentication blade in your app in Azure AD. See screenshot below
Or go to the Manifest blade and make oauth2AllowIdTokenImplicitFlow to true. See screenshot below
Error : OpenIdConnectMessage.Error was not null, indicating an error. Error: 'unsupported_response_type'.
This error occurred because Azure AD not return any Access tokens or ID tokens.
Azure AD need to enabled check box to return tokens, after authentication is done.
How to Solve : goto Azure AD => App registration => click tab Authentication =>
enabled Access tokens and ID tokens check-boxes.
Make sure you don't have two instances of the key oauth2AllowImplicitFlow in your manifest - in my case I had added the key but it was present already with the value set to false. Hopefully this solves the issue:)
I was facing similar issue and when visited the page of ActiveDirectory -> App registrations, it wasnt showing new UI.
Also it doesnt allow me to set the flag in the metadata, Found the workaround for this.
https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Authentication/quickStartType//sourceType/Microsoft_AAD_IAM/appId/9bab1d75-34b8-475b-abfe-5a62c6f01234/objectId/a4b459c1-7753-400c-8f8f-46fb5451234/isMSAApp//defaultBlade/Overview/servicePrincipalCreated/true
First login to your instance, modify the above URL to paste object id and application id of your application.
Then it should show the screen #Mosè Bottacini posted.
It is true like a lot of you are saying that you need to enable ID tokens (used for implicit and hybrid flows) if you really need the ID Token.
'AADSTS700054: response_type 'id_token' is not enabled for the
application.
However if you use a Authorization Code Flow you don't really need it. Microsoft OpenID Connect authentication (Microsoft.AspNetCore.Authentication.OpenIdConnect) uses id_token as default ResponseType for OpenIdConnect and JwtSecurityTokenHandler.
Using AddOpenIdConnect you can set ResponseType to OpenIdConnectResponseType.Code or simply "code" and then you don't need the id_token at all.
Working example with Azure Ad and IdentityServer:
services.AddAuthentication()
.AddOpenIdConnect("aad", "Azure AD", options =>
{
options.ClientSecret = "<ClientSecret>";
options.ResponseType = OpenIdConnectResponseType.Code;
options.ClientId ="<ClientId>";
options.Authority = "https://login.microsoftonline.com/<TenantId>/";
options.CallbackPath = "/signin-oidc";
})
.AddIdentityServerJwt();
http://docs.identityserver.io/en/latest/topics/signin_external_providers.html
I stumbled across this post since I was having the exact same issue with my Azure App Service. I fixed it by using the exact redirect URL in the error message and adding that to the list of URLs in the app registration.

Azure Graph API: authorize application on multiple tenants

I am trying to create an application to browse my contacts directory on Exchange.
I have set up everything and I am able request the authorization from my app.
I can present the modal view, enter the login information, retrieve the token, but when I try to authorize the app with the same account I have created it I get this message:
The client <my app id> and resource <my app URI> identify the same application.
If I try to authorize another account, I receive this message instead:
User account <an email> from external identity provider <a url> is not
supported for application <my app id>
If I try to login on the Graph Explorer Console or on the Office 365 OAuth Sandbox, they work fine with the second address, but not with the first one.
I am really confuse. I feel like I have mess up some configuration option, but I don't really understand which one.
Regarding #1, please do not pass App ID of your application for resource querystring parameter when authenticating against your tenant URL. I ran into the exact same problem.
Then I ran WebApp-MultiTenant-OpenIdConnect-DotNet from Github and noted down the sign-in URL it created and I used the following:
var signInUrl = String.Format(
"https://login.windows.net/{0}/oauth2/authorize?response_mode=form_post&response_type=code+id_token&scope=openid+profile&client_id={1}&resource={2}&redirect_uri={3}&state={4}&nonce={5}",
Uri.EscapeDataString(tenantId),
Uri.EscapeDataString(clientId),
Uri.EscapeDataString("https://graph.windows.net"),
Uri.EscapeDataString(redirectUri),
Uri.EscapeDataString(state),
string.Format("{0}{1}", DateTime.UtcNow.Ticks, Guid.NewGuid().Stringify())
);
Basically I used https://graph.windows.net instead of App ID and magically things started to work :).
Another thing you could try (and I have not tried it) is authenticating against common endpoint https://login.windows.net/common/oauth2/authorize and provide your App ID for the resource querystring.

Unable to authenticate to ASP.NET Web Api service with HttpClient

I have an ASP.NET Web API service that runs on a web server with Windows Authentication enabled.
I have a client site built on MVC4 that runs in a different site on the same web server that uses the HttpClient to pull data from the service. This client site runs with identity impersonation enabled and also uses windows authentication.
The web server is Windows Server 2008 R2 with IIS 7.5.
The challenge I am having is getting the HttpClient to pass the current windows user as part of its authentication process. I have configured the HttpClient in this manner:
var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);
My understanding is that running the site with identity impersonation enabled and then building the client in this manner should result in the client authenticating to the service using the impersonated identity of the currently logged in user.
This is not happening. In fact, the client doesn't seem to be authenticating at all.
The service is configured to use windows authentication and this seems to work perfectly. I can go to http://server/api/shippers in my web browser and be prompted for windows authentication, once entered I receive the data requested.
In the IIS logs I see the API requests being received with no authentication and receiving a 401 challenge response.
Documentation on this one seems to be sparse.
I need some insight into what could be wrong or another way to use windows authentication with this application.
Thank You,
Craig
I have investigated the source code of HttpClientHandler (the latest version I was able to get my hands on) and this is what can be found in SendAsync method:
// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc). Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);
Now if you check within your code the value of SecurityContext.IsWindowsIdentityFlowSuppressed() you will most probably get true. In result the StartRequest method is executed in new thread with the credentials of the asp.net process (not the credentials of the impersonated user).
There are two possible ways out of this. If you have access to yours server aspnet_config.config, you should set following settings (setting those in web.config seems to have no effect):
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
If you can't change the aspnet_config.config you will have to create your own HttpClientHandler to support this scenario.
UPDATE REGARDING THE USAGE OF FQDN
The issue you have hit here is a feature in Windows that is designed to protect against "reflection attacks". To work around this you need to whitelist the domain you are trying to access on the machine that is trying to access the server. Follow below steps:
Go to Start --> Run --> regedit
Locate HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0 registry key.
Right-click on it, choose New and then Multi-String Value.
Type BackConnectionHostNames (ENTER).
Right-click just created value and choose Modify.
Put the host name(s) for the site(s) that are on the local computer in the value box and click OK (each host name/FQDN needs to be on it's own line, no wildcards, the name must be exact match).
Save everything and restart the machine
You can read full KB article regarding the issue here.
I was also having this same problem. Thanks to the research done by #tpeczek, I developed the following solution: instead of using the HttpClient (which creates threads and sends requests async,) I used the WebClient class which issues requests on the same thread. Doing so enables me to pass on the user's identity to WebAPI from another ASP.NET application.
The obvious downside is that this will not work async.
var wi = (WindowsIdentity)HttpContext.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
Note: Requires NuGet package: Newtonsoft.Json, which is the same JSON serializer WebAPI uses.
The reason why this is not working is because you need double hop authentication.
The first hop is the web server, getting impersonation with Windows authentication to work there is no problem. But when using HttpClient or WebClient to authenticate you to another server, the web server needs to run on an account that has permission to do the necessary delegation.
See the following for more details:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
Fix using the "setspn" command:
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/
(You will need sufficient access rights to perform these operations.)
Just consider what would happen if any server was allowed to forward your credentials as it pleases... To avoid this security issue, the domain controller needs to know which accounts are allowed to perform the delegation.
To impersonate the original (authenticated) user, use the following configuration in the Web.config file:
<authentication mode="Windows" />
<identity impersonate="true" />
With this configuration, ASP.NET always impersonates the authenticated user, and all resource access is performed using the authenticated user's security context.

Resources