I am trying to get access to OneDrive for Business using OAuth2.0 authentication protocol. I have followed this example:
http://msdn.microsoft.com/EN-US/library/dn605894(v=office.15).aspx
an this is my code so far:
// Create an authentication context
AuthenticationContext ac = new AuthenticationContext(string.Format("https://login.windows.net/{0}",
ClaimsPrincipal.Current.FindFirst(TenantIdClaimType).Value));
String id = ClaimsPrincipal.Current.FindFirst(TenantIdClaimType).Value;
// Create a client credential based on the application id and secret.
ClientCredential clcred = new ClientCredential(AppPrincipalId, AppKey);
// Using the authorization code acquire an access token.
var arAD = ac.AcquireTokenByAuthorizationCode(code, new Uri(appRedirect), clcred);
I am getting a Authorization code is malformed error. I don't understand why I am getting this message.
Any help would be appreciated!
Had the same issue when I was trying to get authorization code manually and then use it from console application. Not sure why it doesn't work for me.
But when I use it from Web App, the same app which performed authorization and received code in return (because I specified it as returnURL) - everything works.
I suggest you to have a look on this example
https://github.com/AzureADSamples/WebApp-WebAPI-OAuth2-UserIdentity-DotNet
And also this article to understand what's happening under the good:
http://blogs.msdn.com/b/exchangedev/archive/2014/03/25/using-oauth2-to-access-calendar-contact-and-mail-api-in-exchange-online-in-office-365.aspx
P.S.
It's not a OneDrive, but I had exactly the same problem, so if you can make it working with Exchange or GraphApi, then it should work for OneDrive as well.
Authorization code is malformed error is occured if you send the auth code in incorrect format. May be the authorization code which you are sending is encoded value of authcode and session state. you need to seperate both values and send the auth code only. or decode the '&' symbol(seperator) between auth code and session state and send them as two parameters.
The method AcquireTokenByAuthorizationCode(...) from ADAL4j takes only authcode.
Related
I am logging in against identity server and getting a token back to a web application. Then i am getting the token and sending to an api. All works well. But i want to capture this token to use it for subsequent requests without having to call this line in controller actions. How do i do this?
var accessToken = await HttpContext.GetTokenAsync("access_token");
See this question for details now how to achieve that.
Also do check out this article
I want to interact with OneDrive in my WinForms application. Sadly, the Azure quick start samples do not include WinForms, just UWD.
The flow on what I have to do is consistent, namely given my Client ID, I have to obtain an Authentication Code. Given the authentication code, I can then obtain an Access Code, which will allow me to interact in a RESTful way with the OneDrive API. My plan is to have the authentication piece go in a .Net Framework Library and the file IO calls will go in another library that has no user interface access, as it will go in a Windows Service. I would pass the Access Token to the service.
AADSTS50059: No tenant-identifying information found in either the request or implied by any provided credentials.
This error corresponds to the following code fragment that I lifted from the sample .Net Core daemon quick start code.
Note: I was playing around with Scopes as I kept receiving scope errors and I saw one article, whose link I should have kept, which stated to use the API and default scope.
public bool GetRestAuthenticationToken(out string tokenAuthentication)
{
tokenAuthentication = null;
try
{
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create(Authenticate.AppClientId)
.WithClientSecret(Authenticate.AppClientSecret)
.WithAuthority(new Uri(#"https://login.microsoftonline.com/common/oauth2/nativeclient"))
.Build();
string scope = $"onedrive.readwrite offline_access";
System.Collections.Generic.List<string> enumScopes = new System.Collections.Generic.List<string>();
enumScopes.Add("api://<GUID>/.default");
//enumScopes.Add(Authenticate.Scopes[1]);
var result = Task.Run(async () => await app.AcquireTokenForClient(enumScopes).ExecuteAsync()).Result;
...
}
...
}
I believe that I have my application configured properly now on Azure, but am not 100% positive.
API Permissions:
Authentication:
Desktop Applications: https://login.microsoftonline.com/common/oauth2/nativeclient
Desktop Applications: https://login.live.com/oauth20_desktop.srf
Implicit Grants: Access tokens & ID tokens
Live SDK support (Yes)
Default client type (Yes)
Others:
I do have a client secret and kept note of all the Overview GUIDs
Microsoft Doc 1
I tried several different URLs, but only the one not commented out works with the fragment above, but throws the referenced error.
//string redirect_uri = #"https://www.myapp.com/auth";
//string redirect_uri = "https://login.live.com/oauth20_desktop.srf";
string url = #"https://login.microsoftonline.com/common/oauth2/nativeclient";
//string url = $"https://login.live.com/oauth20_authorize.srf?client_id={appClientId}&scope={scope}&response_type=code&redirect_uri={redirect_uri}";
//string url = $"https://login.microsoftonline.com/common/oauth2/v2.0/authorize?" +
// $"client_id={Authenticate.AppClientId}&" +
// $"scope={scope}&" +
// $"response_type=token&" +
// $"redirect_uri={redirect_uri}";
The goal is the same, namely to obtain an access token that I can use with RESTful calls to work with files and/or directories on OneDrive, e.g.
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.GetAsync(...);
You are trying to implement Client credentials grant type to get the access token.
Based on MSAL initialization, Authority is
(Optional) The STS endpoint for user to authenticate. Usually
https://login.microsoftonline.com/{tenant} for public cloud, where
{tenant} is the name of your tenant or your tenant Id.
We assume that your tenant is "myTenent.onmicrosoft.com", then you should set it as https://login.microsoftonline.com/myTenent.onmicrosoft.com here.
I notice that you specify a scope "onedrive.readwrite" in your code. But it's not a valid permission of Microsoft Graph. The default scope of Microsoft Graph is https://graph.microsoft.com/.default.
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.
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.
I'm just finishing authorization and resource server for OAuth2, using DotNetOpenAuth 4.3.4. For testing, I created test client by implementing OAuth2Client.
Because I'm using DNOA for all the communication and request parsing, I'm not sure if I fully understand what is going on under the hood. But this knowledge is very important when I make documentation.
So, could you please explain to me, how client authentification works in DNOA? I use authorization code as grant_type and when I use my test client to exchange code for access_token, the DNOA somehow validate the client_secret and client_id. I downloaded source code for DNOA, but it not helped.
When I set breakpoint to Oauth2 controller(token method) and parse the request as HttpRequestMessage, i see the request contains "grant_type", "code" and "redirect_uri". But where are client_id and client_secret?
Also, can you tell me where I can find any usable documentation for DNOA? I need to create documentation, which will be valid and usable for all platforms, not just C#, which can use DNOA.
Related question:
I somewhere read, that we should not create authorization codes for unauthentificated clients, but this is exactly what DNOA does (since I receive authorization code even if secret is wrong). Is it ok?
Edit:
This is the request I'm trying to read. It is token request made by DNOA client. I can not see the client_id and client_secret under other parameters like "code", "redirect_uri" and "grant_type". I tought they have to be together. Maybe I'm missing something important from http requests and responses.
When I let DNOA to HandleTokenRequest(request) to continue, it is successfully authenticate the client application (fails when bad secret is set in DNOA client app config).
Edit 2
private readonly WebServerClient Client;
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var authorization = Client.ProcessUserAuthorization();
if (authorization != null)
return authorization.AccessToken;
else
return null;
}
This is my implementation of QueryAccessToken. It is from some sample. I think I created this at the beginning and did not change it, because it worked.
Going rought DNOA source I found out it is method from OAuth 1. THis can be the problem. But the question is, why it works ok with right client cerdentials and not working with bad ones.
Final edit
Looks like DNOA client uses http Basic authorization (client_id and secret are in header). But I need the DNOA server to be able to grab these parameters from POST.
If anyone know how to set DNOA to support client_id and client_secret in POST parameters, it would be awesome!
Thank you
The authorization code grant requires two steps.
The first step is the browser redirecting to the identity provider and displaying the logon ui. The authorization code is returned to the browser by the identity provider and then, from the browser to the client application. This step doesn't involve client secret! This is because the end user can debug this part of the flow and she should not learn the value of the client secret.
Then, when the client application has the onetime authorization code, it concacts the token endpoint directly (server-to-server) to exchange the authorization code for authorization token. This is where client id and client secret are used to verify that only legitimate client applications exchange codes for tokens.
The idea behind this flow is to protect the end user from exposing her password to the client application and also protect the client application from exposing its client secret to the end user.
Also note that the authorization code grant flow is the most complicated one as it involves both username/password (provided by the end user) and clientid/client secret (provided by the client application). There are other flows which allow to get the authorization token in slightly different way, namely:
resource owner grant which involves sending username/password directly by end user to the token endpoint of the identity provider. This flow is suited for desktop/mobile/native apps where the logon ui can be customized (but it also can raise suspicions and users could proably refuse to use it)
client credentials flow which involves sending clientid/client secret by the client application to the idntity provider. There is no end user but only the client application authenticating in the identity provider.
More on flows here:
http://aaronparecki.com/articles/2012/07/29/1/oauth2-simplified
As for DNOA, I found it clean and understandable but the docs are lacking. Fortunately, examples are great and although barely documented, you can find almost everything there. Nonetheless, I was able to set up oauth2 identity provider and resource server in three days and support all four oauth2 flows. I am not going to dig deeply into details as this is not what your question is about, however, if you have DNOA specific questions, just ask.
Edit:: regarding your QueryAccessToken implementation, it seems that you are using the WebServerClient internally. In my code I just initialize its properties:
WebServerClient client = ...
client.ClientIdentifier = "client_id";
client.ClientCredentialApplicator =
ClientCredentialApplicator.PostParameter( "client_secret" );
With these two configured, both client_id and client_secret are sent to the token service with the client_secret passed in POST params.