I'm working in an Unity app that needs to connect to a Microsoft Dynamics 365 which uses OAuth2.0. I was trying using UnityWebRequest to retrieve an access token by calling:
https://login.microsoftonline.com/[TENANT_ID]/oauth2/v2.0/token
using something similar to this thread:
OAuth2 Authentication and Operations in Unity
And it works, I'm able to get and access_token, however, when I try to consume the service with the Bearer token, I always get "401 unauthorized".
I then tried to call instead:
https://login.microsoftonline.com/[TENANT_ID]/oauth2/v2.0/authorize
But when I do that, the response is the actual HTML code of the Microsoft login screen. As far I know, getting an auth code requires a user interaction right? but I've been able to get this done by using Microsoft.IdentityModel.Clients.ActiveDirectory package from NuGet in a console C# app without user interaction, so there must be a way right?
I would really appreciate your help on this! thank you!
UPDATE 1 - My code
Get access token
private IEnumerator GetAccessToken(Action<string> result)
{
Dictionary<string, string> content = new Dictionary<string, string>();
//Fill key and value
content.Add("scope", "https://graph.microsoft.com/.default");
content.Add("grant_type", "client_credentials");
content.Add("client_id", "xxxxx");
content.Add("client_secret", "xxxx");
UnityWebRequest www = UnityWebRequest.Post("https://login.microsoftonline.com/[TENANTID]/oauth2/v2.0/token", content);
//Send request
yield return www.Send();
if (!www.isError)
{
string resultContent = www.downloadHandler.text;
TokenClassName json = JsonUtility.FromJson<TokenClassName>(resultContent);
//Return result
result(json.access_token);
}
else
{
//Return null
result("");
}
}
Call API
private IEnumerator GetData(Action<string> result)
{
Dictionary<string, string> content = new Dictionary<string, string>();
//Fill key and value
content.Add("CustomerGroupId", "10");
UnityWebRequest www = UnityWebRequest.Post("https://[ENVIRONMENT].cloudax.dynamics.com/data/TestEntity", content);
string token = null;
yield return GetAccessToken((tokenResult) => { token = tokenResult; });
result(token);
www.SetRequestHeader("Authorization", "Bearer " + token);
www.Send();
if (!www.isError)
{
string resultContent = www.downloadHandler.text;
// Perform additional operations...
}
}
The resource is different for the token and the API call. In the token request, the resource is https://graph.microsoft.com, it's not your destination API. You should request the token for your destination API.
Related
I'm working on a multilanguage project for accademic purpose. I've written a simple Python Client that make requests to an API server written in ASP.NET. The server retrives spotify info about users. The server interacts with a DB filled by a Golang server that only makes scraping on API's exposed from Spotify. I'm aware that it's a misuse and there are better solutions
Clearly, Golang server, in order to make requests to Spotify API's, needs to know the access token returned from spotify Authorization Code Flow. Overlooking about spotify token expire time, the idea is: after user authentication through Identity module of ASP.NET server (using JWT token), associate the access token obtained calling https://accounts.spotify.com/api/token to user's informations. So, i expose an API in ASP.NET server like this
[AllowAnonymous]
[HttpPost("token")]
public async Task<ContentResult> getTokenAsync(string? code = null)
{
//to retrive information about who is the user that making call -> need later for associate spotifytoken
string accessToken = Request.Headers[HeaderNames.Authorization].ToString().Replace("Bearer ", "");
JwtSecurityTokenHandler t = new JwtSecurityTokenHandler();
var token = t.ReadJwtToken(accessToken);
var user = _userManager.FindByIdAsync(token.Subject).Result;
string s = "https://accounts.spotify.com/api/token";
if (code == null)
{
var qb = new QueryBuilder();
qb.Add("response_type", "code");
qb.Add("client_id", _config["SpotiSetting:clientId"]);
qb.Add("scope", "user-read-private user-read-email user-library-read");
qb.Add("redirect_uri", _config["SpotiSetting:redirectUser"]);
qb.Add("show_dialog", "true");
return new ContentResult
{
ContentType = "text/html",
Content = "https://accounts.spotify.com/authorize/" + qb.ToQueryString().ToString()
//Content = JsonConvert.SerializeObject(user.Result)
};
} else
{
//if i'm here, api is the callback designed for spotify
var qb = new QueryBuilder();
qb.Add("grant_type", "authorization_code");
qb.Add("code", code);
qb.Add("redirect_uri", "https://localhost:44345/spotify/token");
var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, s);
req.Content = new FormUrlEncodedContent(qb);
req.Headers.Authorization = new AuthenticationHeaderValue("Basic", "here_my_secret_encoded_CLIENTID:CLIENT_SECRET");
var response = await client.SendAsync(req);
var result = response.Content.ReadAsStringAsync().Result;
AccessToken json = JsonConvert.DeserializeObject<AccessToken>(result);
user.spotifyInformation.authToken = code;
user.spotifyInformation.accessToken = json;
var res = _userManager.UpdateAsync(user);
if (res.IsCompletedSuccessfully)
{
return Content("ok");
}
else
{
Content("Problem");
}
} return Content("");
}
The problem is that the second time that API is invoked, it's spotify that is sending the first authorization token (needed to request access_token), so I lost user information retrived in the first request. Should be better write two distinct API and separate callback from user request?
It's my first question here, so please to have mercy
I recently implemented custom authentication with Azure Mobile App - All the server side works fine and also my web application which is using that mobile app service is working fine. I tested the server-side in details with POSTMAN and with different scenarios, everything works fine until I try to LoginAsync on Xamarin.
When I pass email and password in POSTMAN, I get the following response as a clear indication that it is working
but when I send a request from my app using LoginAsync I get the following error.
Cannot access child value on Newtonsoft.Json.Linq.JValue
My code to send request is fairly simple as following
public async Task<bool> Authenticate()
{
string username = "todo#gmail.com";
string password = "todo";
string message = string.Empty;
var success = false;
var credentials = new JObject
{
["email"] = username,
["password"] = password
};
try
{
MobileServiceUser user = await client.LoginAsync("CustomAuth", credentials);
if (user != null)
{
success = true;
CreateAndShowDialog("OK", "Auth");
}
}
catch (Exception ex)
{
CreateAndShowDialog(ex, "Auth Error");
}
return success;
}
where I am calling it as follows
private MobileServiceClient client;
client = new MobileServiceClient(applicationURL);
await Authenticate();
Any idea why I am getting Cannot access child value on Newtonsoft.Json.Linq.JValue error?
Cheers
EDIT POST
As a workaround, I am temporarily using InvokeApiAsync with JObject.FromObject instead of LoginAsync
await client.InvokeApiAsync("/.auth/login/CustomAuth", JObject.FromObject(credentials), HttpMethod.Post, null);
I am still not sure why LoginAsync does not work - Until I find a solution I will keep using InvokdeApiAsync as a workaround
AFAIK, your initialization for credentials is correct. For the below error:
Cannot access child value on Newtonsoft.Json.Linq.JValue
I checked your testing result via POSTMAN and found that you did not return userId to your client. The essential properties returned to your client would look like as follows:
{
"authenticationToken":"***",
"user":{
"userId":"***"
}
}
When using MobileServiceClient.LoginAsync, the client SDK would internally invoke LoginAsync() method under MobileServiceAuthentication.cs as follows:
JToken authToken = JToken.Parse(response);
// Get the Mobile Services auth token and user data
this.Client.CurrentUser = new MobileServiceUser((string)authToken["user"]["userId"]);
this.Client.CurrentUser.MobileServiceAuthenticationToken = (string)authToken[LoginAsyncAuthenticationTokenKey];
You would find that it would try to extract the userId property under user to construct the MobileServiceUser instance and assign to MobileServiceClient.CurrentUser.
I am able to login and tweet text message but not able to share an image on twitter.
When I upload the image it gives the response, but when I did not post image on twitter, I got the following response.
This is my code :-
private const string UploadMediaURL = "https://upload.twitter.com/1.1/media/upload.json";
public static IEnumerator UploadMedia(string consumerKey, string consumerSecret, AccessTokenResponse response)
{
Dictionary<string, string> parameters = new Dictionary<string,string>();
Texture2D tex= Resources.Load("imag") as Texture2D;
byte[] dataToSave = tex.EncodeToPNG ();
string encoded64ImageData = System.Convert.ToBase64String( dataToSave );
parameters.Add("media_data",encoded64ImageData);
WWWForm form = new WWWForm(); // Add data to the form to post.
form.AddField("media_data", encoded64ImageData );
Dictionary<string, string> headers = new Dictionary<string, string>();
string auth = GetHeaderWithAccessToken("POST", UploadMediaURL, consumerKey, consumerSecret, response, parameters);
Debug.Log ("Auth " + auth);
headers.Add( "Authorization", auth);
headers.Add( "Content-Transfer-Encoding","base64");
WWW web = new WWW(UploadMediaURL,form.data, headers);
yield return web;
Debug.Log ("Response " + web.text);
}
Response :-
{
"media_id":700577726298599424,
"media_id_string":"700577726298599424",
"size":9734,
"expires_after_secs":86400,
"image":
{
"image_type":"image\/png",
"w":435,
"h":159
}
}
This is my Posting Source Code With Media_Id. When I use it without media_id it posts successfully. But when I give a media_id parameter with it, it gives me an error
failed. 401 Unauthorized{"errors":[{"code":32,"message":"Could not authenticate you."}]}
Source Code:-
private const string PostTweetURL = "https://api.twitter.com/1.1/statuses/update.json";
public static IEnumerator PostTweet(string text, string consumerKey, string consumerSecret, AccessTokenResponse response, PostTweetCallback callback)
{
if (string.IsNullOrEmpty(text) || text.Length > 140)
{
Debug.Log(string.Format("PostTweet - text[{0}] is empty or too long.", text));
callback(false);
}
else
{
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("status", text);
WWWForm form = new WWWForm();
form.AddField("status", text);
form.AddField ("media_id", "732498072731713536");
// HTTP header
var headers = new Dictionary<string,string>();
headers["Authorization"] = GetHeaderWithAccessToken("POST", PostTweetURL, consumerKey, consumerSecret, response, parameters);
WWW web = new WWW(PostTweetURL, form.data, headers);
yield return web;
if (!string.IsNullOrEmpty(web.error))
{
Debug.Log(string.Format("PostTweet - failed. {0}\n{1}", web.error, web.text));
callback(false);
}
else
{
string error = Regex.Match(web.text, #"<error>([^&]+)</error>").Groups[1].Value;
if (!string.IsNullOrEmpty(error))
{
Debug.Log(string.Format("PostTweet - failed. {0}", error));
callback(false);
}
else
{
callback(true);
}
}
}
}
It makes perfect sense that this does not appear on twitter. The code you are currently provided is only meant to upload a image to the twitter service, which then can be attached in twitter status updates.
For example, a media_id value can be used to create a Tweet with an attached photo using the statuses/update endpoint. source
So after uploading your image, and receiving the media_id which can be used in the update
Updates the authenticating user’s current status, also known as Tweeting.
Edit
This is my Posting Source Code With Media_Id. When I use it without media_id it posts successfully. But when I give a media_id parameter with it, it gives me an error
failed. 401 Unauthorized{"errors":[{"code":32,"message":"Could not authenticate you."}]}
You are receiving this error because you are not being verified. As the error itself states as well. As mentioned in the update docs there are some things you have to keep in mind when uploading media. As mentioned here, POST and query parameters are not used when calculation an OAuth signature, unlike a general tweet.
Because the method uses multipart POST, OAuth is handled a little differently. POST or query string parameters are not used when calculating an OAuth signature basestring or signature. Only the oauth_* parameters are used.
Why no tweet has some pretty good answers in regards to this authentication.
Hi guys im trying to use dropnet as means to be using dropbox as a cloud storage for my application, and following the 3 step process using the normal 0auth
1.Get Request Token[done]
2.Send user for authorization, and get back verifier[done]
3.Get Access token using the original Request Token and the verifier[issue!]
if i understand the api correctly since i want to use a single account for my storage i need api key,api secret,token and secret.the token and secret seems to be accessible from the third steps and its my trouble.
from the second step i get this url
https://www.dropbox.com/1/oauth/authorize?oauth_token=xxxxxxxxxx
before pressing authenticate to allow my app to use dropbox
from the documentation i read that you need to use this method
UserLogin GetAccessToken(string code, string redirectUri);
i am assuming here xxxxx is the string code
to validate that is the original
so i made code as follows
var accessToken = client.GetAccessToken("xxxxxxx","https://www.dropbox.com/1/oauth/authorize?oauth_token=xxxxxxxxxx);
var ats =accessToken.Secret;
var att = accessToken.Token;
Console.Writeline(ats);
Console.Writeline(att):
in hopes of getting the console to print my secret and token for my acct but it dosent work ?Giving me the error of
An unhandled exception of type 'DropNet.Exceptions.DropboxRestException' occurred in DropNet.dll
help please !
Solved the problem myself,here is the full code
DropNetClient client = new DropNetClient(variable.ApiKey, variable.ApiSecret);
]
var response =client.GetToken();
var t = response.Token;
var s = response.Secret;
Console.WriteLine(s);
Console.WriteLine(t);
var authorizeUrl = client.BuildAuthorizeUrl(new DropNet.Models.UserLogin
{
Secret = s,
Token = t
}
);
DropNetClient client2= new DropNetClient(variable.ApiKey, variable.ApiSecret,t,s);
// Prompt for user to auth
Process.Start(authorizeUrl);
// PRESS KEY AFTER authorization AFTER
Console.ReadKey();
// If the user authed, let's get that token
try
{
var Token = client2.GetAccessToken();
var userToken = Token.Token;
var userSecret = Token.Secret;
Console.WriteLine(userSecret);//ACCESS TOKEN SECRET
Console.WriteLine(userToken);//ACCESS TOKEN
Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine("Exception! " + e.Message);
Console.ReadKey();
}
// save for later
I'm trying to use Google's Calendar API to demo out an OAuth2 integration that we'll need to do with another third party. I'm using the DotNetOpenAuth library, and I've been able to get the initial redirect to Google for the Allow / Deny prompt and get the authorization code back.
I now need to get the access token and refresh token, but I only seem to get an access token back, refresh token is null.
This is my controller action method where Google redirects back to after the user Accepts or Denies:
public ActionResult ProcessResponse(string state, string code, string error)
{
var oAuthClient =
new WebServerClient(
new AuthorizationServerDescription
{
TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"),
ProtocolVersion = ProtocolVersion.V20
},
_applicationId,
_secret)
{
AuthorizationTracker = new TokenManager()
};
var authState = oAuthClient.ProcessUserAuthorization();
var accessToken = authState.AccessToken;
var refreshToken = authState.RefreshToken;
return View(new[] { accessToken, refreshToken });
}
Any ideas?
EDIT:
To get the authorization code, I setup the oAuthClient identically to what I did above, and use this method:
oAuthClient.RequestUserAuthorization(new[] { "https://www.googleapis.com/auth/calendar" }, returnUrl);
I had a similar problem, and solved mine by hand-coding the HttpRequest and HttpResponse handling. See code at: https://stackoverflow.com/a/11361759/29156