Connecting .net 6 application with Identity Server 3 - .net-6.0

We have Identity Server 3 configured for our organization.
I am creating an application in .Net6 and want to connect and show the login page from Identity Server 3. But it seems like there are fair bit of challenges in it.
I am using the following code in .net6 application in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("cookie", options =>
{
options.Cookie.Name = "mvcclient";
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "MyAuthorityUrlGoesHere";
options.RequireHttpsMetadata = false;
options.ClientId = "MyClientIdGoesHere";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.ResponseType = "id_token"; //for implicit flow
});
The page redirects to the identity server but displays the
error - "The client application is not known or is not authorized."
I think it is due to the fact that we need to set "RedirectUri" too in implicit flow, but somehow there is no property named RedirectUri any more :(
I couldnt find any links on google where they talk about connecting .net 6 application to identity server3. Although I was able to connect a .net4.5 application to identity server 3 and it shows a login page and then redirect me back to .net application.
Can someone please guide me through connecting .net 6 application to identity server 3.
Thanks

Ok I found the answer myself in another stack overflow thread:
In asp core there is a subpath silently appended
soo http://localhost:49946/signin-oidc had to be added to the redirect uris on identity server. And we dont need to give redirecturl at client

Related

Add shared authentication to a Classic ASP.NET Web Forms app and a Web API app

I am replacing a shared cookie authentication design with an Open Id Connect one.
Context: The two web apps currently use FormsAuthentication with a shared AuthCookie cookie and machineKeys.
Systems:
A classic Web Forms app, FormsAuthentication, cookie=AuthCookie, hosts /login.aspx
An integrated Web API app, FormsAuthentication, cookie=AuthCookie
Issue 1: Microsoft's Open Id Connect library requires OWIN. I cannot convert the classic Web Forms app to an OWIN app because OWIN requires Integrated pipeline which would break my Spring.NET library that depends on the classic pipeline.
Action 1A: Convert the Web API to OWIN and added support to Open Id Connect.
Action 1B: Move authentication from the classic site to the Web API.
Issue 2: After authentication I must be able to use both systems.
Action 2: On Open Id Connect redirection, the Web API will work with the JWT bearer token stored in an Open Id Connect cookie. The Web API will create an additional cookie (AuthCookie) which the classic app will use.
Issue 3: How do I keep these two cookies in sync? Users must be both logged in or logged out of the two systems. What happens if one cookie is accidently deleted but not the other?
Issue 4: The Web API isn't receives cookies from Microsoft Open Id Connect but subsequent requests to my Web API don't receive cookies and the HttpContext.Current.User isn't being set.
Classic Web Forms code:
<authentication mode="Forms">
<forms loginUrl="webapi/login" name="LycheeAuthCookie" slidingExpiration="true" path="/" />
</authentication>
Web API code:
Startup.cs:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationMode = AuthenticationMode.Passive }); //Web API should return 401 not redirect to /login. The SPA will handle that
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = Config.ApplicationId,
Authority = authority,
RedirectUri = $"webapi/openIdRedirect",
PostLogoutRedirectUri = $"webapi/openIdLogout",
ResponseType = OpenIdConnectResponseType.IdToken,
Scope = OpenIdConnectScope.OpenIdProfile,
AuthenticationMode = AuthenticationMode.Passive, //Web API should return 401 not redirect to /login. The SPA will handle that
});
...
private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context) //Called on Open Id Connect authentication success
{
var newTicket = new FormsAuthenticationTicket(1,
userId,
DateTime.Now.AddMinutes(int.Parse(claims.FindFirst("iat").Value)),
false,
DateTime.Now.AddMinutes(int.Parse(claims.FindFirst("exp").Value)),
false,
"roles and permissions",
FormsAuthentication.FormsCookiePath);
var cookie = new HttpCookie("AuthCookie");
cookie.Value = FormsAuthentication.Encrypt(newTicket);
cookie.Expires = newTicket.Expiration;
HttpContext.Current.Response.Cookies.Add(cookie);
}
Redirect(redirectUrl);
If you host your websites on the same domain, i.e. "https://localhost", and you share the same .AspNet.Cookies, then it will work!
For both websites add the OWIN middleware, set your project to IIS integrated pipeline mode, and add the following Startup.cs file:
Website 1 at "/" - active:
[assembly: OwinStartup(typeof(...MyStartup))]
namespace MyNamespace
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication();
app.UseOpenIdConnectAuthentication();
}
}
}
Website 2 at "/api" - passive:
[assembly: OwinStartup(typeof(...MyStartup))]
namespace MyNamespace
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new Microsoft.Owin.Security.Cookies.CookieAuthenticationOptions {
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType
});
}
}
}

ChatBot did not work in Web Emulator but work well in Local Bot Framework emulator

I developed the ChatBot that integrates with SharePoint On Premise. When I debug the ChatBot in emulator, it work. But When I debug on Web Emulator in Azure and Website Hosted in Company Website by using DirectLine, it did not work.
Does anyone know how to solve it?
Herewith my screenshot.
Left hand side is from Web Emulator, Right hand side is from local Bot Framework Emulator
Update with Source Code (09 December 2019)
XmlNamespaceManager xmlnspm = new XmlNamespaceManager(new NameTable());
Uri sharepointUrl = new Uri("https://mvponduty.sharepoint.com/sites/sg/daw/");
xmlnspm.AddNamespace("atom", "http://www.w3.org/2005/Atom");
xmlnspm.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices");
xmlnspm.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata");
NetworkCredential cred = new System.Net.NetworkCredential("engsooncheah#mvponduty.onmicrosoft.com", "Pa$$w0rd", "mvponduty.onmicrosoft.com");
HttpWebRequest listRequest = (HttpWebRequest)HttpWebRequest.Create(sharepointUrl.ToString() + "_api/lists/getByTitle('" + "data#work" + "')/items?$filter=Keywords%20eq%20%27bloomberg%27");
listRequest.Method = "GET";
listRequest.Accept = "application/atom+xml";
listRequest.ContentType = "application/atom+xml;type=entry";
listRequest.Credentials = cred;
//LINE 136 start from below
HttpWebResponse listResponse = (HttpWebResponse)listRequest.GetResponse();
StreamReader listReader = new StreamReader(listResponse.GetResponseStream());
XmlDocument listXml = new XmlDocument();
listXml.LoadXml(listReader.ReadToEnd());
if (listResponse.StatusCode == HttpStatusCode.OK)
{
Console.WriteLine("Connected");
await turnContext.SendActivityAsync("Connected");
}
// Get and display all the document titles.
XmlElement root = listXml.DocumentElement;
XmlNodeList elemList = root.GetElementsByTagName("content");
XmlNodeList elemList_title = root.GetElementsByTagName("d:Title");
XmlNodeList elemList_desc = root.GetElementsByTagName("d:Description");
//for LINK
XmlNodeList elemList_Id = root.GetElementsByTagName("d:Id");
XmlNodeList elemList_Source = root.GetElementsByTagName("d:Sources");
XmlNodeList elemList_ContentTypeId = root.GetElementsByTagName("d:ContentTypeId");
var attachments = new List<Attachment>();
for (int i = 0; i < elemList.Count; i++)
{
string title = elemList_title[i].InnerText;
string desc = elemList_desc[i].InnerText;
string baseurllink = "https://mvponduty.sharepoint.com/sites/sg/daw/Lists/data/DispForm.aspx?ID=";
string LINK = baseurllink + elemList_Id[i].InnerText + "&Source=" + elemList_Source[i].InnerText + "&ContentTypeId=" + elemList_ContentTypeId[i].InnerText;
//// Hero Card
var heroCard = new HeroCard(
title: title.ToString(),
text: desc.ToString(),
buttons: new CardAction[]
{
new CardAction(ActionTypes.OpenUrl,"LINK",value:LINK)
}
).ToAttachment();
attachments.Add(heroCard);
}
var reply = MessageFactory.Carousel(attachments);
await turnContext.SendActivityAsync(reply);
Update 17 December 2019
I had try using Embedded and Direct Line. But the Error still same.
The Bot is not hosted in SharePoint.
Update 06 January 2020
Its did not work in Azure Bot Services
Based on your description, you can fetch data from it locally. This means your code and logic are all right.
I noticed that your sharePoint URL is : https://mvponduty.sharepoint.com/sites/sg/daw/ and I tried to access it, and also tried to access your whole request URL : https://mvponduty.sharepoint.com/sites/sg/daw/_api/lists/getByTitle('data#work')/items?$filter=Keywords eq 'bloomberg' the response of the two are all 404.
And you said this is an on-prem site , so could you pls have a check that if this site can be reached from a public network?
I assume when you test your code locally, you can access this site as you are in your internal network which will be able to access the on-prem site. However, when you publish your code to Azure, it is not in your internal work anymore: it is in public network so that can't access your on-prem sharePoint site which caused this error.
As we know, bot code is hosted on Azure app service, if this error is caused by the above reason, maybe Azure App Service Hybrid Connections feature will be helpful in this scenario.
ChatBot seems to be working fine? it's sending and receiving messages. There's some code you have that is behaving differently when run locally versus hosted. There's Xml, is it a file or generated? You need check that it's following the same logic and using same data as when it is run locally. Maybe if you paste some of the (non confidential) code where it crashes, we can have more ideas how to help
When you publish your bot, there will be an option as below :
Select Edit App Service Settings. Add only the following details, nothing else :
MicrosoftAppId : <xxxxx>
MicrosoftAppPassword : <xxxxx>
Click Apply, Ok.
Make sure you remove the Microsoft App Id and Microsoft App Password from appsettings.json, so that it works in bot emulator as well.
Now Publish the bot. It will work at both places.
Hope this is helpful.

Security token validation between Identity Server 4 and 3

I am trying to use a IdS4 server on .Net Core 2.0 with an IdS3 webforms client on .Net45.
As I login via the client I get this exception on the client browser.
[SecurityTokenSignatureKeyNotFoundException: IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 2,
Clause[0] = X509ThumbprintKeyIdentifierClause(Hash = 0x6B7ACC520305BFDB4F7252DAEB2177CC091FAAE1),
Clause[1] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
)
',
token: '{"alg":"RS256","kid":"6B7ACC520305BFDB4F7252DAEB2177CC091FAAE1","typ":"JWT",
"x5t":"a3rMUgMFv9tPclLa6yF3zAkfquE"}.{"nbf":1517303703,"exp":1517304003,
"iss":"http://localhost:5000","aud":"webforms","nonce":"636529004845229500.Mjg4YmMxMGEtZjk2MC00YWY5LWJiNTQtYmU0Njg0MDIwYTFhNzczN2Q1ZGMtN2YxYy00NGJmLWJhNzItNTM1ZDc0OTMyNzBj",
"iat":1517303703,"c_hash":"6Sty4gdTWGo4nEo0V_VSVQ","sid":"17936a127b0267d2588646052c4447c6",
"sub":"6498d093-8dc3-4d69-988e-3914d564f4d0","auth_time":1517303700,
"idp":"local","amr":["pwd"]}'.]
I first got this exception without Clause[0] and thought it was because the two samples I was using have different certificates embedded within them.
My attempt to fix this involved creating a new certificate following this guide.
In IdS4 Startup I have
services.AddIdentityServer()
.AddSigningCredential(GetSigningCredential())
and
private X509Certificate2 GetSigningCredential()
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySerialNumber, "3506fe4f69dc22b340e9c2af500d4659", false);
store.Close();
return certs[0];
}
With the clients secret set to the X509 thumbprint.
This seems to be working. On the IdS3 client I cannot find a way to validate the security token, I assume this would be done by validating the certificate?
If anybody could help me understand my issue better that would be great, I cannot find any useful documentation or examples relating to my case so pretty much anything would be helpful.
Thanks in advance.
Turns out I was trying to validate in the wrong places. All i had to do was point to the certificate in the clients Startup.cs.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Configuration = new OpenIdConnectConfiguration()
{
// Other Stuff...
SigningTokens = { new X509SecurityToken(GetX509Certificate2()) },
// More Stuff...
Where GetX509Certificate2() is:
private X509Certificate2 GetX509Certificate2()
{
var store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
return cert = store.Certificates.Find(X509FindType.FindByThumbprint, "**thumbprint**", false)[0];
}

Get response from EWS behind proxy

I have ASP.NET Core application on server behind proxy.
In this app i call EWS to get EmailDetails. Is there any solution to set proxy for this request?
Simply
var wp = new WebProxy("url")
{
Credentials = new NetworkCredential("login", "password"),
BypassProxyOnLocal = true
}
And
_exchangeService.WebProxy = wp;

Can I use the Dynamics CRM 4.0 SDK against a hosted IFD system?

I am running this code (with names and security details obscured). When I do, I get 401 unauthorised. The credentials are that of the user on the hosted server. Is this possible against an IFD system?
var token = new CrmAuthenticationToken();
token.AuthenticationType = 0;
token.OrganizationName = "myorganisation";
CrmService service = new CrmService();
service.Url = "https://myorganisation.dynamicsgateway.com/mscrmservices/2007/crmservice.asmx";
service.CrmAuthenticationTokenValue = token;
service.Credentials = new NetworkCredential("bob.smith", "Password", "HOSTEDCRM");
var request = new RetrieveMultipleRequest();
request.Query = new QueryExpression
{
ColumnSet = new ColumnSet(new string[] { "name" }),
EntityName = "account"
};
var response = service.Execute(request);
I assume this code is outside of the CRM Website? In that case you'll want to add a reference to the discovery service as Mercure points out above. You'll want to execute a RetrieveCrmTicketRequest against the discovery service to get a ticket good for connecting to the Crm Services.
In your CRM Authentication Token you'll want to set the authentication type to 2 (IFD). Then set the CrmTicket property on the token to the ticket you got from your RetrieveCrmTicketResponse.
I also set the URL based on that response, but you could continue to hard code it.
You will want to continue to set the Credentials on the service.
I use a single user to connect to CRM and cache that ticket (an expiration date is in the response from the discovery service). That way I can bypass the discovery service on future requests. There is an error code to look for to go after the ticket again, but I don't have it off hand.
Yes, it's possible, you are only missing a little pieces, the CrmAuthenticationToken.ExtractCrmAuthenticationToken.
Check out this great explaination on Dynamics Forum http://social.microsoft.com/Forums/en-US/crmdevelopment/thread/81f8ba82-981d-40dd-893d-3add67436478

Resources