I am having trouble with facebook authentication for Mobile Services in Azure.
To be more specific, I already have an application that is using Facebook C# SDK and it works fine. I can log on, fetch list of my friends and so. I want to keep using this SDK, but I also want to authenticate for Azure Mobile Service.
So, my plan was, log on with Facebook C# SDK (as I already do today), get the authentication token, and pass it to the MobileServiceClient.LoginAsync() - function. That way, I can still have all the nice features in Facebook C# SDK, and also use the built in authentication system in Mobile Services for Azure.
var client = new FacebookClient();
dynamic parameters = new ExpandoObject();
parameters.client_id = App.FacebookAppId;
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
parameters.response_type = "token";
parameters.display = "popup";
var loginUrl = client.GetLoginUrl(parameters);
WebView.Navigate(loginUrl);
When load is complete, followin is executed:
FacebookOAuthResult oauthResult;
if (client.TryParseOAuthCallbackUrl(e.Uri, out oauthResult) && oauthResult.IsSuccess)
{
var accessToken = oauthResult.AccessToken;
var json = JsonObject.Parse("{\"authenticationToken\" : \"" + accessToken + "\"}");
var user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook, json);
}
However, I get this exception when I call the last line of code above:
MobileServiceInvalidOperationException, "Error: The POST Facebook login request must specify the access token in the body of the request."
I cannot find any information on how to format the accesstoken, I have tried a lot of different keys (instead of "authenticationToken" as you see in my sample). I also have tried just to pass the accesstoken string, but nothing seem to work.
Also, if I use the MobileServiceClient.LoginAsync() for making a brand new login, it works just fine, but it seem silly to force users to log on twice.
Any help is greatly appreciated!
The format expected for the object is {"access_token", "the-actual-access-token"}. Once the login is completed using the Facebook SDK, the token is returned in the fragment with that name, so that's what the Azure Mobile Service expects.
BTW, this is a code which I wrote, based on your snippet, which works. It should handle failed cases better, though, but for the token format, this should be enough
private void btnLoginFacebookToken_Click_1(object sender, RoutedEventArgs e)
{
var client = new Facebook.FacebookClient();
dynamic parameters = new ExpandoObject();
parameters.client_id = "MY_APPLICATION_CLIENT_ID";
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
parameters.response_type = "token";
parameters.display = "popup";
var uri = client.GetLoginUrl(parameters);
this.webView.LoadCompleted += webView_LoadCompleted;
this.webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
this.webView.Navigate(uri);
}
async void webView_LoadCompleted(object sender, NavigationEventArgs e)
{
AddToDebug("NavigationMode: {0}", e.NavigationMode);
AddToDebug("Uri: {0}", e.Uri);
string redirect_uri = "https://www.facebook.com/connect/login_success.html";
bool close = (e.Uri.ToString().StartsWith(redirect_uri));
if (close)
{
this.webView.LoadCompleted -= webView_LoadCompleted;
this.webView.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
string fragment = e.Uri.Fragment;
string accessToken = fragment.Substring("#access_token=".Length);
accessToken = accessToken.Substring(0, accessToken.IndexOf('&'));
JsonObject token = new JsonObject();
token.Add("access_token", JsonValue.CreateStringValue(accessToken));
try
{
var user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);
AddToDebug("Logged in: {0}", user.UserId);
}
catch (Exception ex)
{
AddToDebug("Error: {0}", ex);
}
}
}
Related
I'm trying to get google report activity by calling https://www.googleapis.com/admin/reports/v1/activity/users/all/applications/meet
I created a service account and I have to use the generated private key (json file) as access token.
My code was:
String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/admin/reports/v1/activity/users/all/applications/meet?eventName=call_ended&maxResults=10&access_token=";
String graph = "";
try
{
JSONParser parser = new JSONParser();
JSONObject data = (JSONObject) parser.parse(
new FileReader("C:/Users/Administrateur/Desktop/GoogleApis/Interoperability-googleApis/target/classes/my-first-project-274515-361633451f1c.json"));//path to the JSON file.
String json_private_key = data.toJSONString();
URL urUserInfo = new URL(PROTECTED_RESOURCE_URL + json_private_key);
HttpURLConnection connObtainUserInfo = (HttpURLConnection) urUserInfo.openConnection();
if (connObtainUserInfo.getResponseCode() == HttpURLConnection.HTTP_OK)
{
StringBuilder sbLines = new StringBuilder("");
BufferedReader reader = new BufferedReader(new InputStreamReader(connObtainUserInfo.getInputStream(), "utf-8"));
String strLine = "";
while ((strLine = reader.readLine()) != null)
{
sbLines.append(strLine);
}
graph = sbLines.toString();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
System.out.println("--------------- Result: " + graph);
but I got null value.
Could you please tell me what I misses ?.
Big Thanks.
The Access Token is not part of your request URL. You can read here about the OAuth2 protocol and how it works.
However, Google built an API that enables you to authenticate your requests without worrying about the underlying OAuth2 process.
You should be using the Java Google Reports API to access activities. Here you can find the Java Quickstart that will help you with the first set up of your Java Application.
Here the Java translation of what you are trying to do, using the Google Reports API:
Reports service = new Reports.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
String userKey = "all";
String applicationName = "meet";
String eventName = "call_ended";
Activities result = service.activities().list(userKey, applicationName)
.setEventName(eventName)
.setMaxResults(10)
.execute();
Edit:
Be sure to use the last version of the Java API package. You can find the Java API docs here: https://developers.google.com/resources/api-libraries/documentation/admin/reports_v1/java/latest/
If you are using Gradle be sure to have this line in the dependencies parameter.
dependencies {
...
compile 'com.google.apis:google-api-services-admin-reports:reports_v1-rev89-1.25.0'
}
References
OAuth2
Google Reports API
I use Xamarin.Auth to authenticate with the OneDrive Service. This worked fine for a while now, but I seems there where changes on the service so it stopped working..
I upgraded to the new version 2.0 and try to make it work again. The Initial authentication works well so far. But after a while it always started to crash. I realized that there isn't any refrehs token sent back from the onedrive service.
This is the code to call the Auth UI:
private Task<IDictionary<string, string>> ShowWebView()
{
var tcs = new TaskCompletionSource<IDictionary<string, string>>();
var auth = new OAuth2Authenticator(ServiceConstants.MSA_CLIENT_ID,
string.Join(",", ServiceConstants.Scopes),
new Uri(GetAuthorizeUrl()),
new Uri(ServiceConstants.RETURN_URL));
auth.Completed +=
(sender, eventArgs) =>
{
tcs.SetResult(eventArgs.IsAuthenticated ? eventArgs.Account.Properties : null);
};
var intent = auth.GetUI(Application.Context);
intent.SetFlags(ActivityFlags.NewTask);
Application.Context.StartActivity(intent);
return tcs.Task;
}
private string GetAuthorizeUrl()
{
var requestUriStringBuilder = new StringBuilder();
requestUriStringBuilder.Append(ServiceConstants.AUTHENTICATION_URL);
requestUriStringBuilder.AppendFormat("?{0}={1}", ServiceConstants.REDIRECT_URI,
ServiceConstants.RETURN_URL);
requestUriStringBuilder.AppendFormat("&{0}={1}", ServiceConstants.CLIENT_ID,
ServiceConstants.MSA_CLIENT_ID);
requestUriStringBuilder.AppendFormat("&{0}={1}", ServiceConstants.SCOPE,
WebUtility.UrlEncode(string.Join(" ", ServiceConstants.Scopes)));
requestUriStringBuilder.AppendFormat("&{0}={1}", ServiceConstants.RESPONSE_TYPE, ServiceConstants.CODE);
return requestUriStringBuilder.ToString();
}
The Authorize URI is:
https://login.live.com/oauth20_authorize.srf?redirect_uri=https://login.live.com/oauth20_desktop.srf&client_id=["id"]&scope=onedrive.readwrite+wl.offline_access+wl.signin&response_type=code
The response I get contains 6 Elements:
access_token: "EwAIA..."
token_type: "bearer"
expires_in: "3600"
scope: "onedrive.readwrite wl.offline_access wl.signin wl.basic wl.skydrive wl.skydrive_update onedrive.readonly"
user_id: "41...."
state: "ykjfmttehzjebqtp"
When I check it with the Documentation (https://dev.onedrive.com/auth/msa_oauth.htm) I can't see what's wrong here. Any ideas?
I called wrong constructor. This one works:
authenticator = new OAuth2Authenticator(ServiceConstants.MSA_CLIENT_ID,
ServiceConstants.MSA_CLIENT_SECRET,
string.Join(",", ServiceConstants.Scopes),
new Uri(ServiceConstants.AUTHENTICATION_URL),
new Uri(ServiceConstants.RETURN_URL),
new Uri(ServiceConstants.TOKEN_URL));
With these constants:
Scopes = {"onedrive.readwrite", "wl.offline_access", "wl.signin"};
RETURN_URL = "https://login.live.com/oauth20_desktop.srf";
AUTHENTICATION_URL = "https://login.live.com/oauth20_authorize.srf";
TOKEN_URL = "https://login.live.com/oauth20_token.srf";
I'm having no luck getting a response from v4 of the Google Sheets API when running against a public (i.e. "Published To The Web" AND shared with "Anyone On The Web") spreadsheet.
The relevant documentation states:
"If the request doesn't require authorization (such as a request for public data), then the application must provide either the API key or an OAuth 2.0 token, or both—whatever option is most convenient for you."
And to provide the API key, the documentation states:
"After you have an API key, your application can append the query parameter key=yourAPIKey to all request URLs."
So, I should be able to get a response listing the sheets in a public spreadsheet at the following URL:
https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}?key={myAPIkey}
(with, obviously, the id and key supplied in the path and query string respectively)
However, when I do this, I get an HTTP 401 response:
{
error: {
code: 401,
message: "The request does not have valid authentication credentials.",
status: "UNAUTHENTICATED"
}
}
Can anyone else get this to work against a public workbook? If not, can anyone monitoring this thread from the Google side either comment or provide a working sample?
I managed to get this working. Even I was frustrated at first. And, this is not a bug. Here's how I did it:
First, enable these in your GDC to get rid of authentication errors.
-Google Apps Script Execution API
-Google Sheets API
Note: Make sure the Google account you used in GDC must be the same account you're using in Spreadsheet project else you might get a "The API Key and the authentication credential are from different projects" error message.
Go to https://developers.google.com/oauthplayground where you will acquire authorization tokens.
On Step 1, choose Google Sheets API v4 and choose https://www.googleapis.com/auth/spreadsheets scope so you have bot read and write permissions.
Click the Authorize APIs button. Allow the authentication and you'll proceed to Step 2.
On Step 2, click Exchange authorization code for tokens button. After that, proceed to Step 3.
On Step 3, time to paste your URL request. Since default server method is GET proceed and click Send the request button.
Note: Make sure your URL requests are the ones indicated in the Spreadsheetv4 docs.
Here's my sample URL request:
https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID?includeGridData=false
I got a HTTP/1.1 200 OK and it displayed my requested data. This goes for all Spreadsheetv4 server-side processes.
Hope this helps.
We recently fixed this and it should now be working. Sorry for the troubles, please try again.
The document must be shared to "Anyone with the link" or "Public on the web". (Note: the publishing settings from "File -> Publish to the web" are irrelevant, unlike in the v3 API.)
This is not a solution of the problem but I think this is a good way to achieve the goal. On site http://embedded-lab.com/blog/post-data-google-sheets-using-esp8266/ I found how to update spreadsheet using Google Apps Script. This is an example with GET method. I will try to show you POST method with JSON format.
How to POST:
Create Google Spreadsheet, in the tab Tools > Script Editor paste following script. Modify the script by entering the appropriate spreadsheet ID and Sheet tab name (Line 27 and 28 in the script).
function doPost(e)
{
var success = false;
if (e != null)
{
var JSON_RawContent = e.postData.contents;
var PersonalData = JSON.parse(JSON_RawContent);
success = SaveData(
PersonalData.Name,
PersonalData.Age,
PersonalData.Phone
);
}
// Return plain text Output
return ContentService.createTextOutput("Data saved: " + success);
}
function SaveData(Name, Age, Phone)
{
try
{
var dateTime = new Date();
// Paste the URL of the Google Sheets starting from https thru /edit
// For e.g.: https://docs.google.com/---YOUR SPREADSHEET ID---/edit
var MyPersonalMatrix = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/---YOUR SPREADSHEET ID---/edit");
var MyBasicPersonalData = MyPersonalMatrix.getSheetByName("BasicPersonalData");
// Get last edited row
var row = MyBasicPersonalData.getLastRow() + 1;
MyBasicPersonalData.getRange("A" + row).setValue(Name);
MyBasicPersonalData.getRange("B" + row).setValue(Age);
MyBasicPersonalData.getRange("C" + row).setValue(Phone);
return true;
}
catch(error)
{
return false;
}
}
Now save the script and go to tab Publish > Deploy as Web App.
Execute the app as: Me xyz#gmail.com,
Who has access to the app: Anyone, even anonymous
Then to test you can use Postman app.
Or using UWP:
private async void Button_Click(object sender, RoutedEventArgs e)
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(#"https://script.google.com/");
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("utf-8"));
string endpoint = #"/macros/s/---YOUR SCRIPT ID---/exec";
try
{
PersonalData personalData = new PersonalData();
personalData.Name = "Jarek";
personalData.Age = "34";
personalData.Phone = "111 222 333";
HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(personalData), Encoding.UTF8, "application/json");
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(endpoint, httpContent);
if (httpResponseMessage.IsSuccessStatusCode)
{
string jsonResponse = await httpResponseMessage.Content.ReadAsStringAsync();
//do something with json response here
}
}
catch (Exception ex)
{
}
}
}
public class PersonalData
{
public string Name;
public string Age;
public string Phone;
}
To above code NuGet Newtonsoft.Json is required.
Result:
If your feed is public and you are using api key, make sure you are throwing a http GET request.In case of POST request, you will receive this error.
I faced same.
Getting data using
Method: spreadsheets.getByDataFilter has POST request
I have a Java Servlet backend with a datastore connected to my app; I am trying to implement a login system using the Android Studio LoginActivity template, using the user's email and password (not the PlusBaseActivity handling the Google Account login), but I don't know how to proceed from here:
How can you say that a User is logged in? and how can I make it so persistently using my datastore? I've read here: How to login User using UserService on AppEngine Java that I just need to call the method resp.sendRedirect(userService.createLoginURL(req.getRequestURI())), and I've done so:
#Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
int size = checkDatastore(); // 0 if empty, > 0 if not empty
if(size==0){
populateDatastore();
}
String asyncMessage = req.getParameter("order");
if(asyncMessage.equals("login")){
mail = req.getParameter("email");
psw = req.getParameter("password");
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
String message="";
resp.setContentType("text/plain");
PrintWriter out = resp.getWriter();
if(user == null) {
//Sends a temporary redirect response to the client using the
// specified redirect location URL and clears the buffer.
String uri = userService.createLoginURL(req.getRequestURI());
resp.sendRedirect(uri);
User user1 = userService.getCurrentUser();
message="No one is logged in!\n" + "Sent from App Engine at " + new Date();
out.println(message);
out.flush();
}if(user !=null) {
// login(user);
message = "Hello, " + user.getEmail() +
", "+user.getNickname()+"!" + "\nSent from App Engine at "+ new Date();
out.println(message);
out.flush();
}
}
}
but the sendRedirect() method only gives me a URI. What for?
Moreover, the User user = userService.getCurrentUser() always returns null. How come?
That's because the resp.sendRedirect(userService.createLoginURL(req.getRequestURI()))
of UserService only works when integrating the Login with Google Accounts as shown in this documentation.
If you want to implement a personalised login system you can do that in many ways. Surely you will need a Servlet checking new users' data and a datastore to persistently store new account registrations.
Within my app (following various documentations) the user is able to login in to facebook via a dedicated login page. From this, the access token is given and the I save it. The thing is to get the token this way (using await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, loginUri, new Uri(redirectUri));, which seems the automatically show a login screen from facebook, I honestly have no idea how it appears...) means the user has to type their username and password again.
I don't want this to be the case, since I think if the user has logged in once, they shouldn't need to keep typing in their details again and again. However no matter what I've tried I can seem to get a new access token without going through this login screen again.
I've tried methods using the FacebookClient.Get() method, however it just doesn't exist in this version of the SDK. I have no idea what to use in its place, or what I need to send to Facebook to get a response.
So far this is what I have to log in for the first time (pretty much what is on the documentation)
string redirectUrl = "https://www.facebook.com/connect/login_success.html";
try
{
var loginUrl = fb.GetLoginUrl(new
{
client_id = FacebookHandler.FacebookAppId,
redirect_uri = redirectUrl,
scope = FacebookHandler.Permissions,
display = "touch",
response_type = "token"
});
Uri endUri = new Uri(redirectUrl);
WebAuthenticationResult webAuthRes = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, loginUrl, endUri);
if (webAuthRes.ResponseStatus == WebAuthenticationStatus.Success)
{
var callbackUri = new Uri(webAuthRes.ResponseData.ToString());
var facebookOAuthResult = fb.ParseOAuthCallbackUrl(callbackUri);
var accessToken = facebookOAuthResult.AccessToken;
if (String.IsNullOrEmpty(accessToken))
{
// User is not logged in, they may have canceled the login
}
else
{
// User is logged in and token was returned
FacebookHandler.AccessToken = accessToken;
LoginSucceeded();
}
}
else if (webAuthRes.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
Debug.WriteLine("Error: " + "HTTP Error returned by AuthenticateAsync() : " + webAuthRes.ResponseErrorDetail.ToString());
}
else
{
Debug.WriteLine("Error: " + "Error returned by AuthenticateAsync() : " + webAuthRes.ResponseStatus.ToString());
}
}
catch (Exception ex)
{
//
// Bad Parameter, SSL/TLS Errors and Network Unavailable errors are to be handled here.
//
Debug.WriteLine(ex);
throw ex;
}
On successive launches of the app, after the user as entered their details for the first time, the saved access token could be used, but it soon expires, which throws a FacebookOAuthException. This is where I have tried to invisibly get a new access token, and where its not working.
try
{
await fb.PostTaskAsync(url, argList);
}
catch (FacebookOAuthException exc)
{
Debug.WriteLine(exc.Message);
ValidateLoginUrl(GetLoginUrl());
//Problem is with the ValidateLoginUrl(), which would get an access token from the login url
}
One of the things I've tried was to use TryParseOAuthCallbackUrl(), on the loginUrl that I can get by using FacebookClient.GetLoginUrl() (which is successful), but when I try to parse it it just comes up with an InvalidOperationException, and otherwise no explanation.
What do I have to do in order to get a new access token without the user having to login in again? I have my app ID and secret, and I could probably save the users ID from the initial login for use later(?). Would any of that help do this?