I want to verify user profile from Google Authorization code sent by android client, to do that, we have to download client_secrets.json and put it inside our rails app. Just like this tutorial https://developers.google.com/identity/protocols/OAuth2WebServer
But when I try to follow this step
require 'google/apis/drive_v2'
require 'google/api_client/client_secrets'
client_secrets = Google::APIClient::ClientSecrets.load
auth_client = client_secrets.to_authorization
auth_client.update!(
:scope => 'https://www.googleapis.com/auth/drive.metadata.readonly',
:redirect_uri => 'http://www.example.com/oauth2callback',
:additional_parameters => {
"access_type" => "offline", # offline access
"include_granted_scopes" => "true" # incremental auth
}
)
Rails throws an error said "No client_secrets.json filename supplied and/or could not be found in search path."
The errors shows up even though I have insert client_secrets.json inside config/client_secrets.json
Do you know what's the problem or what's the alternative for this solution, thank you.
It seems, ClientSecrets.load accepts optional argument which is config filename path. So I believe it's okay if you specify filename directly:
Google::APIClient::ClientSecrets.load("#{Rails.root}/config/client_secrets.json")
Related
I have an Android application in which I'm using Azure AD B2C to authenticate users. Users login and logout of the application as needed. I would like to give the user the option to delete their own account.
I understand that I need to use the Azure AD Graph API to delete the user. This is what I have so far:
According to this link, it looks like deleting a user from a personal account (which is what the B2C users are using) is not possible. Is that correct?
Here's my code snippet for the Graph API call. Feel free to ignore it if I'm off track and there is a better way to solve this.
I believe I need a separate access token than what my app currently has (as the graph API requires other API consent). So, I'm getting the access token as follows:
AcquireTokenParameters parameters = new AcquireTokenParameters.Builder()
.startAuthorizationFromActivity(getActivity())
.fromAuthority(B2CConfiguration.getAuthorityFromPolicyName(B2CConfiguration.Policies.get("SignUpSignIn")))
.withScopes(B2CConfiguration.getGraphAPIScopes())
.withPrompt(Prompt.CONSENT)
.withCallback(getGraphAPIAuthCallback())
.build();
taxApp.acquireToken(parameters);
In the getGraphAPIAuthCallback() method, I'm calling the Graph API using a separate thread (in the background):
boolean resp = new DeleteUser().execute(authenticationResult.getAccessToken()).get();
Finally, in my DeleterUser() AsyncTask, I'm doing the following:
#Override
protected Boolean doInBackground(String... aToken) {
final String asToken = aToken[0];
//this method will be running on background thread so don't update UI from here
//do your long running http tasks here,you dont want to pass argument and u can access the parent class' variable url over here
IAuthenticationProvider mAuthenticationProvider = new IAuthenticationProvider() {
#Override
public void authenticateRequest(final IHttpRequest request) {
request.addHeader("Authorization",
"Bearer " + asToken);
}
};
final IClientConfig mClientConfig = DefaultClientConfig
.createWithAuthenticationProvider(mAuthenticationProvider);
final IGraphServiceClient graphClient = new GraphServiceClient.Builder()
.fromConfig(mClientConfig)
.buildClient();
try {
graphClient.getMe().buildRequest().delete();
} catch (Exception e) {
Log.d(AccountSettingFragment.class.toString(), "Error deleting user. Error Details: " + e.getStackTrace());
}
return true;
}
Currently, my app fails when trying to get an access token with a null pointer exception:
com.microsoft.identity.client.exception.MsalClientException: Attempt to invoke virtual method 'long java.lang.Long.longValue()' on a null object reference
Any idea what I need to do to provide the user the option to users to delete their own account? Thank you!
Thanks for the help, #allen-wu. Due to his help, this azure feedback request and this azure doc, I was able to figure out how to get and delete users silently (without needing intervention).
As #allen-wu stated, you cannot have a user delete itself. So, I decided to have the mobile app call my server-side NodeJS API when the user clicks the 'Delete Account' button (as I do not want to store the client secret in the android app) and have the NodeJS API call the Azure AD endpoint to delete the user silently. The one caveat is that admin consent is needed the first time you try to auth. Also, I have only tested this for Graph API. I'm not a 100% sure if it works for other APIs as well.
Here are the steps:
Create your application in your AAD B2C tenant. Create a client secret and give it the following API permissions: Directory.ReadWrite.All ;
AuditLog.Read.All (I'm not a 100% sure if we need the AuditLog permission. I haven't tested without it yet).
In a browser, paste the following link:
GET https://login.microsoftonline.com/{tenant}/adminconsent?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&state=12345
&redirect_uri=http://localhost/myapp/permissions
Login using an existing admin account and provide the consent to the app.
Once you've given admin consent, you do not have to repeat steps 1-3 again. Next, make the following call to get an access token:
POST https://login.microsoftonline.com/{B2c_tenant_name}.onmicrosoft.com/oauth2/v2.0/token
In the body, include your client_id, client_secret, grant_type (the value for which should be client_credentials) and scope (value should be 'https://graph.microsoft.com/.default')
Finally, you can call the Graph API to manage your users, including deleting them:
DELETE https://graph.microsoft.com/v1.0/users/{upn}
Don't forget to include the access token in the header. I noticed that in Postman, the graph api had a bug and returned an error if I include the word 'Bearer' at the start of the Authorization header. Try without it and it works. I haven't tried it in my NodeJS API yet, so, can't comment on it so far.
#allen-wu also suggested using the ROPC flow, which I have not tried yet, so, cannot compare the two approaches.
I hope this helps!
There is a line of code: graphClient.getUsers("").buildRequest().delete();
It seems that you didn't put the user object id in it.
However, we can ignore this problem because Microsoft Graph doesn't allow a user to delete itself.
Here is the error when I try to do it.
{
"error": {
"code": "Request_BadRequest",
"message": "The principal performing this request cannot delete itself.",
"innerError": {
"request-id": "8f44118f-0e49-431f-a0a0-80bdd954a7f0",
"date": "2020-06-04T06:41:14"
}
}
}
Framework: Appium + Protractor + Cucumber + Typescript using POM model
I have the protractor framework for appium designed in POM structure
The initial page of the app will identify the locators calling in a different ts file and the functions such as tap, isDisplayed calling it in a different ts file.
But once it passes the initial pages in the app,say 3 pages. the locators are not identified which are calling other function, but they are identified when they are passed directly ( like driver.findelements(by.css('')).click ) this works.
The problem is I can't pass this code like this within the step definition .ts file always as it is not a good structure
Note: By the way, this script was working fine earlier.
tried to test using a different workaround, like building the binary again, trying to run on android and ios application, downgrading or upgrading the node packages. But nothing solved the problem. has anyone faced this kind of issue. Any suggestions or solutions for this problem, please?
Code which works: (Passing the locators directly in the function, rather than from the onboarding.ts file will work)
Then(/^VIC should be selected from the state or territory drop down$/, async () => {
await browser.driver.findElement(by.css('button[sp-automation-id=\'select-state-toggle\']')).click();
await browser.driver.findElement(by.css('page-action-sheet label[sp-automation-id=\'action-sheet-option-VIC\']')).click(); });
Code which does not work: (Onboarding.ts file contains the locators defined for State and VIC same as the above code block. But reading from there it does not work.)
Then(/^VIC should be selected from the state or territory drop down$/, async () => {
await AutomationAction.tap(Onboarding.State);
await AutomationAction.tap(Onboarding.VIC); });
Code which works (The below code is called before the above code block, it's a page before calling the above pages)
Then(/^I enter the mobile number and tap next button on the your mobile number screen$/, async () => {
MobileNo = AutomationAction.getMobileNumber("mobileNumber");
SameMobileNo = MobileNo;
await AutomationAction.sendKeyText(Onboarding.InputMobileNo,MobileNo);
await AutomationAction.tap(Onboarding.Next_BTN_YourMobileNumber);
});
Because of the page where it is failing the automation thinks its as non-angular page and the locators used to fail or not locate them when calling it in a different function. When I introduced browser.ignoreSycnhronization=true to make Angular sync for non-angular apps/pages it worked.
Let me start by saying I've found several proposed solutions online, but none of them seem to work for me.
Issue:
I have a meteor app I'm trying to run on android. For this, I've deployed the app on Heroku and I call the run android-device command using the --mobile-server https://myapp.heroku.com parameter.
I permanently receive the error
"XMLHttpRequest cannot load https://myapp.heroku.com/sockjs/... . No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:12848' is therefore not allowed access. The response had HTTP status code 404.", source: http://localhost:12848/ (0)
Here is what I've tried so far:
I set ROOT URL at meteor startup:
process.env.ROOT_URL = "https://myapp.heroku.com";
I tried setting the Access Control like this, server-side at meteor startup:
WebApp.connectHandlers.use(function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
res.header('Access-Control-Allow-Origin', 'https://myapp.heroku.com');
res.header('Access-Control-Allow-Origin', 'http://localhost:12848');
res.header('Access-Control-Allow-Origin', 'http://meteor.local');
res.header("Access-Control-Allow-Headers", "Content-type,Accept,X-Custom-Header");
return next();
});
I tried to use the browser-policy package, like this, server-side at meteor startup:
BrowserPolicy.content.allowSameOriginForAll();
BrowserPolicy.content.allowOriginForAll('*');
BrowserPolicy.content.allowOriginForAll('http://meteor.local');
BrowserPolicy.content.allowOriginForAll('https://myapp.heroku.com');
BrowserPolicy.content.allowOriginForAll('https://*.myapp.heroku.com');
BrowserPolicy.content.allowEval();
I tried adding access rules to "mobile-config.js":
App.accessRule("*");
I made sure the name in the "package.json" file under root is identical to the App name under "mobile-config.js"
What else am I missing?
Edit:
I've also tried adding the express and cors packages to whitelist local host:
var whitelist = [
'http://localhost:3000',
'http://localhost:12848',
'https://myapp.heroku.com'
];
var corsOptions = {
origin: function(origin, callback){
var originIsWhitelisted = whitelist.indexOf(origin) !== -1;
callback(null, originIsWhitelisted);
},
credentials: true
};
app.use(cors(corsOptions));
Also tried to enable pre-flight, like this:
app.options('*', cors())
This is probably the stupidest problem I ever ran into. Trying to run the app using the --mobile-server https://myapp.heroku.com parameter was wrong. Instead, it should be https://myapp.herokuapp.com
That was it. That was the problem all along...
Adding '*' to whitelist should do the job. The ultimate solution lies within config.xml, this should be helpful: https://stackoverflow.com/a/36124935/8056323
I am using davibennun/laravel-push-notification in laravel framework for push notification, it worked well in iphone device but in android it gives
"Unable to enable crypto on TCP connection gcm-http.googleapis.com"
I also tried with
$deviceToken = "dxu8XvChIXg:APA91bEUASmBE_bpDzHlgvf9RZhLYE4W7ni5xeRZ-ze-QKE-1uUFbTcWkGJ05GkbTWbcU8tceA_rnUwDgBr8w46cylkkvz9ORoCmdqSt8AoVviGRdQbcM1GtbDZqWdcSpyJpZPSX2SRP";
$push = PushNotification::app('Androidpush');
$push->adapter->setAdapterParameters(['sslverifypeer' => false]);
$push->to($deviceToken)->send('Hello World, i`m a push message');
What I am doing wrong...??
Please suggest me if you already done.
Thanks in advance
I don't see any error with the code you provided as compared with this related issue. This could be an issue with how your http adapter is configured. Try to use the curl adapter, and/or make sure that the sslcapath is set to a valid location. Check this thread which one of the community also encounter the issue on Android devices.
Change the
vendor/sly/notification-pusher/src/Sly/NotificationPusher/Adapter/Gcm.php
Gcm.php -> public function getOpenClient(....){
$client->setApiKey($this->getParameter('apiKey'));
$new_client = new \Zend\Http\Client(null, array(
'adapter' => 'Zend\Http\Client\Adapter\Socket',
'sslverifypeer' => false
));
$client->setHttpClient($new_client);
return $client;
}
I'm developing a Sinatra app for which I'd like to use OmniAuth. So far, I have something similar to this for the web app:
http://codebiff.com/omniauth-with-sinatra
I'd like the web app to be usable via Android phones which would use an API, authenticating by means of a token. The development of an API seems to be covered nicely here:
Sinatra - API - Authentication
What is not clear is now I might arrange the login procedure. Presumably it would be along these lines:
User selects what service to use, e.g. Twitter, FaceBook &c., by means of an in-app button on the Android device.
The Android app opens a webview to log in to the web app.
A token is somehow created, stored in the web app's database, and returned to the Android app so that it can be stored and used for subsequent API requests.
I'm not very clear on how point 3 might be managed - does anyone have any suggestions?
As no-one seems to have any suggestions, here's what I've come up with so far. I don't think it's very good, though.
I've added an API key to the user model, which is created when the user is first authenticated:
class User
include DataMapper::Resource
property :id, Serial, :key => true
property :uid, String
property :name, String
property :nickname, String
property :created_at, DateTime
property :api_key, String, :key => true
end
....
get '/auth/:name/callback' do
auth = request.env["omniauth.auth"]
user = User.first_or_create({ :uid => auth["uid"]},
{ :uid => auth["uid"],
:nickname => auth["info"]["nickname"],
:name => auth["info"]["name"],
:api_key => SecureRandom.hex(20),
:created_at => Time.now })
session[:user_id] = user.id
session[:api_key] = user.api_key
flash[:info] = "Welcome, #{user.name}"
redirect "/success/#{user.id}/#{user.api_key}"
end
If the authorisation works then the api_key is supplied to the Android app, which will presumably store it on the device somewhere:
get '/success/:id/:api_key', :check => :valid_key? do
user = User.get(params[:id],params[:api_key])
if user.api_key == params[:api_key]
{'api_key' => user.api_key}.to_json
else
error 401
end
end
All API calls are protected as in the link in my original post:
register do
def check (name)
condition do
error 401 unless send(name) == true
end
end
end
helpers do
def valid_key?
user = User.first(:api_key => params[:api_key])
if !user.nil?
return true
end
return false
end
end
For public use I'll only allow SSL connections to the server. Any suggestions for improvement would be welcome.