how to load OAuth certificate in dot net core - asp.net-core-mvc

we are implementing the new application in dot net core, in the part we need to use open id authentication with our SecureAuth server. I am unable to figure out how to load the certificate into OAuthOptions.
here is my code to use OAuth
var cert = new X509Certificate2(Path.Combine(env.ContentRootPath, "/Connect.cer"));
var saOptions = new OAuthOptions
{
AuthenticationScheme = "ACR Connect",
ClientId = Configuration["AppSettings:ConnectSettings:ClientID"],
ClientSecret = Configuration["AppSettings:ConnectSettings:ClientSecret"],
TokenEndpoint = Configuration["AppSettings:ConnectSettings:CallbackPath"],
CallbackPath = Configuration["AppSettings:ConnectSettings:TokenEndpoint"],
AuthorizationEndpoint = Configuration["AppSettings:ConnectSettings:TokenEndpoint"],
UserInformationEndpoint = Configuration["AppSettings:ConnectSettings:UserInformationEndpoint"],
ClaimsIssuer = Configuration["AppSettings:ConnectSettings:ClaimsIssuer"],
};
app.UseOAuthAuthentication(saOptions);
When we are implementing in MVC5 we are pretty much sure about syntax, but How to do in MVC6(Dot Net Core 1.0).

Related

ConfidentialClientApplicationBuilder with userTokenCache

I am unable to use ConfidentialClientApplicationBuilder with userTokenCache.
Code in samples look something like this but this code is obsolete now and I am supposed to use ConfidentialClientApplicationBuilder.
ConfidentialClientApplication app;
var request = httpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, _azureAdOptions.CallbackPath ?? string.Empty);
var credential = new ClientCredential(_azureAdOptions.ClientSecret);
TokenCache userTokenCache = _tokenCacheProvider.GetCache(httpContext, claimsPrincipal, authenticationProperties, signInScheme);
string authority = $"{_azureAdOptions.Instance}{_azureAdOptions.TenantId}/";
app = new ConfidentialClientApplication(_azureAdOptions.ClientId, authority, currentUri, credential, userTokenCache, null);
return app;
ConfidentialClientApplicationBuilder Code
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(_azureAdOptions.ClientId)
.WithAuthority(authority)
.WithRedirectUri(currentUri)
.WithCertificate(clientCertificate)
.Build();
Its done a bit differently now.
You initialize the TokenCache implementation separately and attach it to the app object. see this line for reference.
Its highly recommended you study how the Token Cache is best implemented for MSAL. The TokenCacheProviders folder has the implementations.

Using Network Creds in dotnet core app on a mac using HttpClient

Writing a dotnet core app. I need to log in with network credentials as the service (which happens to be a TFS on-prem server) uses those to authenticate. From my (and another team members') windows machine, the following code works:
Console.WriteLine("Type in your DOMAIN password:");
var pass = GetPassword(); //command line secure string magic from SO
var networkCredential = new NetworkCredential("USERNAME", pass, "DOMAINNAME");
string tfsDefaultCollection = "https://TFSURL/DefaultCollection";
string testUrl = $"{tfsDefaultCollection}/_apis/tfvc/changesets/1234/changes?api-version=2.2";
var httpClientHandler = new HttpClientHandler
{
Credentials = networkCredential
};
var client = new HttpClient(httpClientHandler)
{
BaseAddress = new Uri(testUrl)
};
httpClientHandler.PreAuthenticate = true;
var test = client.GetAsync(testUrl).Result;
Console.WriteLine(test);
But it doesn't work from my mac. I get a 401 unauthorized. Both used the same, hardwired connection. AND this works on my mac:
curl --ntlm --user "DOMAINNAME\USERNAME" "https://TFSURL/DefaultCollection/_apis/tfvc/changesets/1234/changes?api-version=2.2"
So that rules out a connectivity question, I would think. Am I missing something I need to be doing on my mac? Can anybody point me to some documentation or way to troubleshoot what both of these requests are doing at the lowest level to see if there is a difference?
Well finally some google-foo got me there. There's a bug in dotnet core for linux/mac. This issue describes the fix:
https://github.com/dotnet/corefx/issues/25988#issuecomment-412534360
It has to do with the host machine you are connecting to uses both Kerberos and NTLM authentication methods.
Implemented below:
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
Console.WriteLine("Type in your DOMAIN password:");
var pass = GetPassword(); //command line secure string magic from SO
var networkCredential = new NetworkCredential("USERNAME", pass, "DOMAINNAME");
string tfsDefaultCollection = "https://TFSURL/DefaultCollection";
string testUrl = $"{tfsDefaultCollection}/_apis/tfvc/changesets/1234/changes?api-version=2.2";
var myCache = new CredentialCache
{
{
new Uri(testUrl), "NTLM",
networkCredential
}
};
var httpClientHandler = new HttpClientHandler
{
Credentials = myCache
};
var client = new HttpClient(httpClientHandler)
{
BaseAddress = new Uri(testUrl)
};
httpClientHandler.PreAuthenticate = true;
var test = client.GetAsync(testUrl).Result;
Console.WriteLine(test);
Thanks to #dmcgill50 for getting me on the right googling track.

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];
}

Google AUTH API Application Type, how important is it?

I've been doing a lot tinkering around with the authentication stuff using the .NET libraries provided by Google.
We have both a desktop and web-app side, and what we want to achieve is to authenticate ONCE, either on the desktop or the web side, and store the refresh token, and reuse it both on the web side and the desktop side.
So the situation is like so, on the desktop side, when there's no saved existing AccessToken's and RefreshToken's, we will ask the user to authenticate via this code:
using (var stream = new FileStream("client_secrets_desktop.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
new[] { GmailService.Scope.GmailReadonly, GmailService.Scope.GmailCompose },
"someemail#gmail.com", CancellationToken.None);
}
In this case the Client ID and Secret is of an Application type Installed Application.
On the web-application side, if there's also no refresh token yet then I'm using DotNetOpenAuth to trigger the authentication, here's the code snippet:
const string clientID = "someclientid";
const string clientSecret = "somesecret";
const string redirectUri = "http://localhost/Home/oauth2callback";
AuthorizationServerDescription server = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"),
TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
ProtocolVersion = ProtocolVersion.V20
};
public ActionResult AuthenticateMe()
{
List<string> scope = new List<string>
{
GmailService.Scope.GmailCompose,
GmailService.Scope.GmailReadonly,
GmailService.Scope.GmailModify
};
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
// Here redirect to authorization site occurs
OutgoingWebResponse response = consumer.PrepareRequestUserAuthorization(
scope, new Uri(redirectUri));
response.Headers["Location"] += "&access_type=offline&approval_prompt=force";
return response.AsActionResult();
}
public void oauth2callback()
{
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
consumer.ClientCredentialApplicator =
ClientCredentialApplicator.PostParameter(clientSecret);
IAuthorizationState grantedAccess = consumer.ProcessUserAuthorization(null);
string accessToken = grantedAccess.AccessToken;
}
Here is where I want to confirm my suspicions.
When there is a RefreshToken that exists, we use the following code snippet to call the Gmail API's
UserCredential uc = new UserCredential(flow, "someemail#gmail.com", new TokenResponse()
{
AccessToken = "lastaccesstoken",
TokenType = "Bearer",
RefreshToken = "supersecretrefreshtoken"
});
var refreshState = await uc.RefreshTokenAsync(CancellationToken.None);
var svc = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = uc,
ApplicationName = "Gmail Test",
});
Here's the thing I noticed is that, for me to be able to use the refresh token to refresh from either the desktop or the web side, the refresh token needs to be generated through the same client ID/secret combination. I've tested it and it seems like it's fine if we use Installed application as the application type for the Client ID for both the desktop and the web, but my question I guess is, these application type's for the client IDs, do they matter so much?
Am I doing anything wrong to do it this way?
Thanks in advance

Migration of Google Apps Marketplace app to oAuth 2.0 with additional scopes

We have app using oauth 1.0 in old marketplace. We are in process of migrating to oauth 2.0 for new marketplace. We are using UpgradeableApp API to do migration for existing domains. I am following steps specified here : https://developers.google.com/apps-marketplace/v1migratev2
As mentioned in the prerequisites in the above link: The scopes for the new and old apps must be compatible. But our new app has some additional scopes. Is there any way to grant access to these additional scopes while doing migration.
Only domain's admin or users can approve additional scopes.
Domain's admin receives an email notification after upgrade.
In your oauth2.0 app you can detect if all scopes have been approved or not. If not, you can show the user appropriate message to contact domain admin to get scopes approved.
For this we should have same scope in both old as well as on new listing. I am also facing the same problem of migrating the old users to new one. Kindly check the below code how I am migrating from old to new Users but every time I am getting 401 UnAuthorized, May I know what I am missing for this.
String url = String.Format("https://www.googleapis.com/appsmarket/v2/upgradableApp/{0}/{1}/{2}", oldAppId, chromeListing, domain);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "PUT";
request.ContentType = "application/json";
request.Accept = "application/json";
request.ProtocolVersion = HttpVersion.Version11;
request.Credentials = CredentialCache.DefaultCredentials;
request.Headers.Add("Authorization", "OAuth");
Hashtable postObj = new Hashtable();
postObj["Consumer Key"] = oldClientId;
postObj["Consumer Key Secret"] = oldSecret;
String s1 = new JavaScriptSerializer().Serialize(postObj);
var bs = Encoding.UTF8.GetBytes(s1);
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(bs, 0, bs.Length);
}
using (WebResponse response = request.GetResponse())
{
using (var sr = new StreamReader(response.GetResponseStream()))
{
result = sr.ReadToEnd();
sr.Close();
}
}

Resources