I'm using the below code to get access token and refresh token from docusign.
But I'm always getting the invalid-grant error. I'm pasting the code below.
[HttpGet("GetDocToken")]
[AllowAnonymous]
public async Task<IActionResult> getToken(string docCode)
{
var x = docCode.Length;
var client = new HttpClient();
var authCode=Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("ced8998a-4387-4f30-9ab7-51c0d1af49bf:d7c3ccd4-22fa-4f18-a540-ddf11d8b2c9f"));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authCode);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
var requestContent = new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", docCode),
new KeyValuePair<string, string>("redirect_uri", "http://localhost:4200/auth")
});
HttpResponseMessage response = await client.PostAsync("https://account-d.docusign.com/oauth/token", requestContent);
string resultContent = response.Content.ReadAsStringAsync().Result;
return Ok(response.Content.ReadAsStringAsync());
}
My assumption is that you have received the authentication code back from the DocuSign identity system and are trying to exchange it for an access token.
A couple of issues:
The docs incorrectly indicates that the redirect_uri should be included in the request. The Example Request in the docs does correctly show that the request should only include the grant_type and code parameters.
Note: While the OAuth standard, section 4.1 (d) does indicate that the redirect_url should be included, DocuSign usually does not include it.
My guess is that DocuSign will ignore the redirect_uri parameter, but you might want to try leaving it out.
Another issue is timing: The authorization code you receive back from DocuSign is only good for a minute or so. If you're not immediately using the authorization code (your code's docCode) then you'll get the Invalid Grant error.
Known good example software
I suggest that you also check out the known-good code example for C#. You can use a protocol peeker to see exactly what it is doing during the authentication.
Use a library
I also suggest that you look for an OAuth Authorization Code client library that you can use instead of rolling your own.
For example, are you setting and checking the state value? It's important to do that to stop CSRF attacks. See this article.
Added
It is also not clear to me that you are using the right value as the authorization code.
I believe the flow should be:
User presses "Authenticate with DocuSign" in the Angular app.
User's browser does a GET to the DocuSign authentication server. At this point, the browser is no longer running the Angular app.
User's browser and DocuSign Authentication server exchange HTML back and forth as the user authenticates with DocuSign.
The user completes the authentication process with DocuSign.
DocuSign sends a REDIRECT response to the browser, telling the browser to do a GET on the redirect url. The redirect (and the GET) include query parameters for code and state
Your SERVER (not the Angular app), receives the GET request.
Your server should:
Extract the code and state query parameters
Verify that state is the same as was sent in step 2.
Makes the POST request to DocuSign to exchange the authorization code for an access token.
RESPONDS to the browser with the Angular program.
The Angular program is now once again running on the browser.
Use Implicit Grant
Your other option is to use Implicit Grant. That way you don't need a server component. With Implicit Grant, your Angular program handles the flow.
Related
I have am trying to do a get request to api with blockcypher.com. In the docs they simply append the api token to the URL with ?token= or if it's part of multiple parameters &token=. What's the proper protocol for putting this in the headers to make sure that the token isn't sent in the URL for security reasons?
I already tried to do this formula
var client = _clientFactory.CreateClient(nameof(<Parent Function Name>));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", <API Token Here>);
This didn't work. My blockcypher account claims that no requests to my API were sent when I use the above code.
If the API you're calling doesn't support the Authorization header, there isn't anything you can do. From looking at BlockCypher's docs, they only appear to support passing the token as a URL parameter. The Authorization header is very standard, so not supporting it is odd.
This isn't as big of a security risk as you might think, as long as you are only doing server-to-server communication (not a browser or mobile app). When you use HTTPS, the request URL is encrypted (see Is an HTTPS query string secure?).
I want to sign out a user in OneDrive API, I tried this, I sent the request:
var client = new RestClient("https://login.live.com/oauth20_logout.srf?client_id=762d0c10-xxxx-xxxx-xxxx-085a4a1743bc&redirect_uri=urn:ietf:wg:oauth:2.0:oob");
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(response.IsSuccessful);
output:
302
False
my question is how to send a logout request
OAuth is inherently stateless so there is really nothing to "sign out" of. When you complete the OAuth flow you receive a token back. That token is used to authenticate the user every time you call the API. If you don't include the token in the Authorization header, the API will reject your request.
So to "sign out", simply wipe any stored access token values from your app's memory/storage and the app will no longer have access to that user's account.
I am afraid you did not follow the rules before performing the request:
Delete any cached access_token or refresh_token values you've
previously received from the OAuth flow.
Perform any sign out actions in your application (for example,
cleaning up local state, removing any cached items, etc.).
Only after can you make a call to the authorization web service using the url:
https://login.microsoftonline.com/common/oauth2/v2.0/logout?post_logout_redirect_uri={redirect-uri}
After removing the cookie, the browser will be redirected to the redirect URL you provided. When the browser loads your redirect page, no authentication query string parameters will be set, and you can infer the user has been logged out.
We currently have a client(MVC .Net Core Web Application) and web API which is used to access Microsoft Graph calls, the authentication process uses Azure AD v2.0 endpoint. we can get this working if we do all of this in the client, however, as soon as we pass the token to the Web API this fails and doesn't even return a response to help us diagnose. below are loads of things we have tried, we have come to the conclusion our azure hasn't been set up correctly but unsure.
Process 1 Works
We forget the web API and do all actions inside the client, this method works but not what we are after so we know the code is not broken.
Process 2 Fails
Client Authenticates to Microsoft graph
redirects with code to the client
send the code to Web API to authenticate, get access token and refresh token
Fails returns nothing
Process 3 Fails
Client Authenticates to Microsoft graph
redirects with code to the client
send the code in the client to authenticate, get access token and refresh token
send access token and refresh token to Web API
use token to perform Microsoft graph action
Fails returns nothing
Azure Settings
Within Microsoft Azure, we have tried configuring the Microsoft Active Directory to follow this example. However, this uses WPF and can't get this to work. We follow it step by step and even the user is prompted with the API permissions be still doesn't work. I am happy to provide any further information to help diagnose this issue.
Our architecture we are trying to achieve.
Update
This is where we exchange the authorization code for an access token however, this gives no return response and times out after about 100 seconds, doesn't even continue.
HttpContent content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"code", code},
{"client_id", _clientId},
{"client_secret", _clientSecret},
{"redirect_uri", _redirectUri},
{"grant_type", _codeGrantType}
});
using (HttpClient client = new HttpClient())
{
var response = await client.PostAsync(_tokenServerUrl, content);
if (response.IsSuccessStatusCode)
{
//get token from body and update expiry time...
var token = await response.Content.ReadAsJsonAsync<OAuthToken>();
token.ExpiresOn = DateTime.Now.AddSeconds(token.ExpiresIn - 100);
return token;
}
Not sure if this is really silly question, but is it possible to access / utilize the Dropbox within the scope of a web API.
Take the following into consideration. I have a multi-tenant front-end application written in Angular. The back-end (multi-tenant) in a C# web API. The user can upload documents / files. The API will be responsible for uploading the documents to the storage provider. In this case it's Dropbox. The thing is, I have read that Dropbox no longer supports username / password auth. Thus OAuth is used were by the user needs to provide consent to the calling application. This only has to happen once. Since multiple users within a single tenant can utilize the online storage, I'm struggling to see how this would work through a web API...
Any suggestions?
Thanks!
Yes, you can utilize 3rd party API inside Web API. I have never used Drop box API, but as you are saying they use OAuth as authentication mechanism i can show you the code which calls the 3rd party API in this case it is (clarifai).
i am using HttpClientyou can use Dropbox .NET SDK. However if you want to stick to HTTP then here is the documentation for the same.
string token = "Oauth token received using some mechanism";
string requestPath = string.Format("v1/tag");
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://api.clarifai.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
HttpResponseMessage response = await client.PostAsync(requestPath, content);
if (response.IsSuccessStatusCode)
{
var resString = await response.Content.ReadAsStringAsync();
// JSON Response
JObject resJsonObject = JObject.Parse(resString);
}
Now, As you have the concern to authenticate the user and get the token. Here is Dropbox example in which they show how to redirect a user on dropbox website to authenticate and get the temporary oauth token.
Take a look at Connect Action Method and AuthAsync Action Method in Home Controller.
Hope this helps you. If any doubt feel free to ask.
I have a mobile client (app) letting the user authenticate with google. So, the client receives an access token and some info (name, email etc) from google. Works fine!
I also created an ASP.NET Web API that the mobile app should comunicate with. On the client side I am adding the token to the HttpClient with:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "pretty_long_access_token_separated_by_two_dots");
Question 1: I'm trying to "decode" the access token on this site (to make sure it's all right): https://jwt.io/
The header and the payload is all right, but it seems like it's an "invalid signature" (says in the bottom). Should I worry about this?
On the server side, I added this to the Configuration method in the Startup class:
app.UseJwtBearerAuthentication( new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new List<string> {"my_client_id"},
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(#"https://accounts.google.com/", "my_client_secret")
},
});
The only thing I want to do with the token, on my server side, is making sure that only validated users from my app should be able to access my API-controller.
Question 2: Is UseJwtBearerAuthentication the right thing for me, or am I going in the wrong direction?
My problem is, I constantly get 401, unauthorized, when trying to access my WEB API controller.
If I am on the right track, I can try to explain more about the server side setup...
Any helt would be very appreciated!
If you are using a JWT token then you will need JWT instead of Bearer
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("JWT", "pretty_long_access_token_separated_by_two_dots");
The signature is used to validate the token as authentic and is therefore only required by the authentication server. If Google is your authentication server, then there should be an API endpoint you can call from your server to verify that the token is valid.
If your server is issuing the JWT token, then you will need to check that the token is valid by decoding the signature using the secret that was used to create it in the first place
If Google is issuing the JWT and you want your server to be able to self validate it, then you need to use another encryption type such as RS256 which will allow you to validate the signature using a public key issued by Google (I have no idea if they provide this method or not)
The reason https://jwt.io/ cannot validate your signature is because it was signed using a secret code. If you have this secret code then you are able to paste it into the textbox in the bottom right. If the secret is correct, and the token hasn't expired, it will show as being a valid JWT token.