Why is my DotNetOpenAuth consumer not respecting the version 1.0a? - dotnetopenauth

I am building an OAuth service provider using DotNetOpenAuth, and to test it I have modified the sample wcf consumer to simply call a plain http endpoint. The token request works fine, but when I request access to a protected resource, I get the following protocol execption:
The following required parameters were missing from the DotNetOpenAuth.OAuth.Messages.AuthorizedTokenRequest message: oauth_verifier
When I look at the log output on my service provider I see this:
Error while performing basic validation of AuthorizedTokenRequest with these message parts:
oauth_token: pgzjBIs0pKCeDIcaIinyrV5Jhi0=
oauth_consumer_key: sampleconsumer
oauth_nonce: TM0Rc8kg
oauth_signature_method: HMAC-SHA1
oauth_signature: zmpxK5c69n1VzTEEcrnnd4e+qYI=
oauth_version: 1.0
oauth_timestamp: 1305067751
Notice the oauth_version: 1.0, even though I have specified ProtocolVersion.V10a when I create the consumer.
If I specify ProtocolVersion.V10 on both sides I get this exception:
Expected message DotNetOpenAuth.OAuth.Messages.AccessProtectedResourceRequest but received DotNetOpenAuth.OAuth.Messages.AuthorizedTokenRequest instead.
Here is the consumer code to get the token (this is straight from the sample code):
WebConsumer consumer = this.CreateConsumer();
UriBuilder callback = new UriBuilder(Request.Url);
callback.Query = null;
string[] scopes = (from item in this.scopeList.Items.OfType<ListItem>()
where item.Selected
select item.Value).ToArray();
string scope = string.Join("|", scopes);
var requestParams = new Dictionary<string, string> { { "scope", scope } };
var response = consumer.PrepareRequestUserAuthorization(callback.Uri, requestParams, null);
consumer.Channel.Send(response);
Here is my consumer code that is failing:
var accessToken = Session["WcfAccessToken"] as string;
var consumer = CreateConsumer();
var serviceEndpoint = new MessageReceivingEndpoint("https://mymachine/test/getUserName", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest);
var httpRequest = consumer.PrepareAuthorizedRequest(serviceEndpoint, accessToken);
var httpResponse = httpRequest.GetResponse();
In my service provider I call serviceProvider.ReadProtectedResourceAuthorization(); and it fails with the exception I mentioned above.
Any ideas what I am doing wrong?

This was a silly mistake on my part, I was returning the wrong TokenType, from my IServiceProviderTokenManager. The correct logic is shown in the service provider sample, and looks something like this:
if (tokenObject.State == TokenAuthorizationState.AccessToken)
return TokenType.AccessToken;
return TokenType.RequestToken;

Related

Sign In using raw HttpRequestMessage in ASP.NET MVC

I have been testing some code to sign in users to their Microsoft/school/work accounts using raw HttpRequestMessage and HttpResponseMessage. I know there are libraries available to do this but I want to test the raw approach as well (especially usage of refresh tokens), while looking for the right library to handle it.
I'm currently learning authentication, with limited knowledge of ASP.NET/Core.
I'm following this guide: https://learn.microsoft.com/en-us/graph/auth-v2-user
I've just modified the SignIn() method in AccountController in an example project that used more high level libraries to sign in.
I'm requesting an authorization code.
The SignIn() code:
public void SignIn()
{
using (var httpClient = new HttpClient())
{
try
{
var tenant = "my tenant id";
var clientId = ConfigurationManager.AppSettings["ida:AppID"];
var responseType = "id_token+code";
var redirectURI = ConfigurationManager.AppSettings["ida:RedirectUri"];
var responseMode = "form_post";//query";
var appScopes = ConfigurationManager.AppSettings["ida:AppScopes"];
var scopes = $"openid profile offline_access {appScopes}";
var state = "12345";
//var prompt = "consent";
var url = string.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/authorize", tenant);
var body = string.Format("client_id={1}&response_type={2}&redirect_uri={3}&response_mode={4}&scope={5}&state={6}", tenant, clientId, responseType, redirectURI, responseMode, scopes, state);
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");
var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead).Result;
var content = response.Content.ReadAsStringAsync().Result;
}
catch (Exception ex)
{
}
}
//if (!Request.IsAuthenticated)
//{
// // Signal OWIN to send an authorization request to Azure
// Request.GetOwinContext().Authentication.Challenge(
// new AuthenticationProperties { RedirectUri = "/" },
// OpenIdConnectAuthenticationDefaults.AuthenticationType);
//}
}
I'm just returning void from the method now because I'm not sure what I should return yet.
Debugging and looking at the response variable, the status code is 200, and has some other information to it. However, the content of the HttpResponseMessage, when I paste it into a file and opening it in a browser, displays (or redirects to) https://login.microsoftonline.com/cookiesdisabled, which shows a message saying that I could not be logged in because my browser blocks cookies. However, I don't think this really is the case.
How can I resolve this and have the user log in and consent, and get the authorization code?
I couldn't really find any example in ASP.NET that uses this raw approach. Is it not recommended?
You should fistly understand how OAuth 2.0 authorization code flow works in Azure AD V2.0 :
Microsoft identity platform and OAuth 2.0 authorization code flow
The general process would be like :
When login in client application, user will be redirect to Azure AD login endpoint(https://login.microsoftonline.com/{0}/oauth2/v2.0/authorize) and provides info like which client(client_id) in which tenant(tenant id) user wants to login , and redirect back to which url(redirect_uri) after successful login.
User enter credential , Azure AD validate credential and issue code and redirect user back to redirect url provided in step 1 (Also match one of the redirect_uris you registered in the portal).
The client application will get the code and send http post request with code to acquire access token .
So if you want to manally implement the code flow in your application , you can refer to below code sample :
public async Task<IActionResult> Login()
{
string authorizationUrl = string.Format(
"https://login.microsoftonline.com/{0}/oauth2/v2.0/authorize?response_type=code&client_id={1}&redirect_uri={2}&scope={3}",
"tenantID", "ClientID", "https://localhost:44360/Home/CatchCode",
"openid offline_access https://graph.microsoft.com/user.read");
return Redirect(authorizationUrl);
}
private static readonly HttpClient client = new HttpClient();
public async Task<ActionResult> CatchCode(string code)
{
var values = new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "client_id", "XXXXXX"},
{ "code", code},
{ "redirect_uri", "https://localhost:44360/Home/CatchCode"},
{ "scope", "https://graph.microsoft.com/user.read"},
{ "client_secret", "XXXXXXXXXXX"},
};
var content = new FormUrlEncodedContent(values);
//POST the object to the specified URI
var response = await client.PostAsync("https://login.microsoftonline.com/cb1c3f2e-a2dd-4fde-bf8f-f75ab18b21ac/oauth2/v2.0/token", content);
//Read back the answer from server
var responseString = await response.Content.ReadAsStringAsync();
//you can deserialize an Object use Json.NET to get tokens
}
That just is simple code sample which will get Microsoft Graph's access token , you still need to care about url encode and catch exception , but it shows how code flow works .

DocuSign "invalid_grant" on posting jwtToken

I'm trying to achieve "Service Integration Authentication" following the steps here docusign docs and it's doing fine until Requesting the Access Token, where you send the jwt token (which is well formed)
I'm always getting "invalid_grant", and according to that doc, is because some of the claims are invalid. Is there another cause for that error?
All the claims looks good
C#:
//request access token
var client3 = new RestClient("https://" + _host);
var request3 = new RestRequest("/oauth/token", Method.POST);
request3.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request3.AddParameter("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
var headers = new[]
{
new Claim("alg", "RS256"),
new Claim("typ", "JWT"),
}.ToList();
var claims = new[] {
new Claim("iss", _integrationKey), //<-- integration key
new Claim("sub", OAuthGrant.Sub), //<-- returned from /oauth/userinfo (OK)
new Claim("iat", ToUnixTime(DateTime.Now).ToString(), ClaimValueTypes.Integer64),
new Claim("exp", ToUnixTime(DateTime.Now.AddHours(1)).ToString(), ClaimValueTypes.Integer64),
new Claim("aud", _host), //<-- "account-d.docusign.com"
new Claim("scope", "signature"),
}.ToList();
//build jwt from private key. token decodes just fine from https://jwt.io/
var jwtToken = CreateToken(claims, headers, "private-key.pem", Server.MapPath("/"));
request3.AddParameter("assertion", jwtToken);
System.Diagnostics.Debug.WriteLine("jwtToken:" + jwtToken);
var response = client3.Execute<OAuthToken>(request3);
System.Diagnostics.Debug.WriteLine("response content:" + response.Content); //<-- getting "invalid_grant"
return response.Data;
The jwt token was validated using https://jwt.io/ and decodes just fine.
Is docusign demo sandbox
Thanks in advance
daniel
My assumption is the library which you are using is generating wrong assertion for you. You can check DS SDK as well - ConfigureJwtAuthorizationFlow method in DS SDK, it will help you in generating the Assertion in correct way as expected by DS APIs.

How to get an ACS app-only access token for Project Online

I'm trying to get an AppOnly access token for use in the Authorization Bearer header of my request to a REST endpoint in Project Online (SharePoint). Following is a snippet of the code that I was using to retrieve the access token.
private OAuth2AccessTokenResponse GetAccessTokenResponse()
{
var realm = TokenHelper.GetRealmFromTargetUrl([[our_site_url]]);
var resource = $"00000003-0000-0ff1-ce00-000000000000/[[our_site_authority]]#{realm}";
var formattedClientId = $"{ClientId}#{realm}";
var oauth2Request = OAuth2MessageFactory.CreateAccessTokenRequestWithClientCredentials(
formattedClientId,
ClientSecret,
resource);
oauth2Request.Resource = resource;
try
{
var client = new OAuth2S2SClient();
var stsUrl = TokenHelper.AcsMetadataParser.GetStsUrl(realm);
var response = client.Issue(stsUrl, oauth2Request) as OAuth2AccessTokenResponse;
var accessToken = response.AccessToken;
}
catch (WebException wex)
{
using (var sr = new StreamReader(wex.Response.GetResponseStream()))
{
var responseText = sr.ReadToEnd();
throw new WebException(wex.Message + " - " + responseText, wex);
}
}
}
I keep getting 403 Forbidden as the response from the server, even if I include site collection admin credentials with my request. Does anyone out there have any ideas?
After creating a support ticket with Microsoft to figure this out we eventually decided to move away from using app permissions for console application authorization.
Our workaround was to create SharePointOnlineCredentials object using a service account, and then get the Auth cookie from the credentials object to pass with our WebRequest. This solution came from scripts found here: https://github.com/OfficeDev/Project-REST-Basic-Operations

DotNetOpenAuth - UserAgentClient::ExchangeUserCredentialForToken - How to pass the redirect_uri parameter

Trying to retrieve the OAuth2.0 AccessToken using DotNetOpenAuth library, coded the same as below -
https://github.com/DotNetOpenAuth/DotNetOpenAuth/wiki/Security-scenarios
private static IAuthorizationState GetAccessTokenFromOwnAuthSvr()
{
var server = new AuthorizationServerDescription();
server.TokenEndpoint = new Uri("https://localhost/STS/OAuth/Token");
server.ProtocolVersion = DotNetOpenAuth.OAuth2.ProtocolVersion.V20;
var client = new UserAgentClient(server, clientIdentifier: "RP");
client.ClientCredentialApplicator =
ClientCredentialApplicator.PostParameter("data!");
var token = client.ExchangeUserCredentialForToken(
"Max Muster", "test123", new[] { "http://localhost/demo"});
return token;
}
This is not working as AuthZ Server returns error, complaining about the missing redirect_uri.
Going through few of the links here, saw that an AuthorizationState has the redirect_uri option, but could not figure out how to pass the AuthorizationState object in the ExchangeUserCredentialForToken request.
Is there a way to send the redirect_uri parameter or the AuthorizationState object as part of the ExchangeUserCredentialForToken request ?
Thanks in advance
--Abraham V K

IdentityServer3 Response status code does not indicate success: 400 (Bad Request)

I always get Bad Request 400 from IdentityServer3. I am trying for 3 days now but no luck :( Anyone could please tell me what am I doing wrong?
I am trying to access IdentityServer3 hosted by another vendor that I have no control. The vendor has asked us to implement Implement OAuth2 authentication with Bearer token. The vendor provided us with the Client ID, Client Secret and the URL to be used is http://www.xxxxxx.com/identity/connect/token
The vendor told us to use to request bearer token and use it in the request headers Authorization: Bearer
I can successfully obtain the bearer token from vendor. But when I call the
GET /api/profiles/myemailaddress#gmail.com I get Bad Request 400
Here is what I have done:
TokenClient client = new TokenClient("http://www.xxxxxx.com/identity/connect/token", "myclientid", "myclientsecret", AuthenticationStyle.PostValues);
var response = await client.RequestResourceOwnerPasswordAsync("myemailaddress#gmail.com", "mypassword", "profile"); // successfully gives me the token
i got the access token, now i want to use the token to request user profile:
var clienthttp = new HttpClient();
clienthttp.BaseAddress = new Uri("http://www.xxxxxx.com");
clienthttp.SetBearerToken(response.AccessToken);
var json = await clienthttp.GetStringAsync("http://www.xxxxxx.com/api/profiles/myemailaddress#gmail.com"); // error Bad Request 400
Additional Info:
"scopes_supported":["profile","offline_access"],
"claims_supported":[]
Thank you.
The vendor was expecting additional value in the header. Since my request was missing that additional value, they returned Bad Request. I had to modify my code to find the exact reason of bad request.
Here is the updated code, might be useful for someone:
var client = new HttpClient();
client.BaseAddress = new Uri("http://www.xxxxx.com");
client.SetBearerToken(response.AccessToken);
var callApiResponse = client.GetAsync("api/profiles/myemailaddress#gmail.com").Result;
string tokenresponse = callApiResponse.StatusCode.ToString();
string clientresult = callApiResponse.Content.ReadAsStringAsync().Result;
tokenresponse: "Bad Request 400"
clientresult: "Missing CompanyID in the header"
Then I knew that they also expect companyid in the header so I added it. then all was good.
client.DefaultRequestHeaders.Add("CompID", "xxxxxx");
I had a similar error (Response status code does not indicate success: 400 (Bad Request)) for different resource not identity server. i manage to resolve that using FormUrlEncodedContent
Refer below code
using (HttpClient client = new HttpClient())
{
string baseUrl = "https://*******.com/****"
Dictionary<string, string> jsonValues = new Dictionary<string, string>();
jsonValues.Add("username", "******");
jsonValues.Add("password", "******");
var contenta = new FormUrlEncodedContent(jsonValues);
var response = await client.PostAsync(baseUrl, contenta);
using (HttpContent content = response.Content)
{
string data = await content.ReadAsStringAsync();
if (data != null)
{
Console.WriteLine(data);
}
}
}

Resources