In my use case, there is a registration page that triggers the browser-specific webauthn flow. For example in Chrome on a Mac you will see this series of popups:
Pick an option between USB security key and Built-in sensor
MacOS confirmation with Touch ID
Confirmation dialog from Chrome requesting access to your security key
Besides https://w3c.github.io/webauthn/#add-virtual-authenticator I haven't found much documentation about authenticating with webauthn as part of a selenium test. What resources are available to help devs figure out how to test webauthn with Selenium in JavaScript? I have also checked out https://github.com/SeleniumHQ/selenium/issues/7829 but the example test case does not make sense to me. Examples would be hugely appreciated.
Update with solution for js:
import { Command } from 'selenium-webdriver/lib/command';
addVirtualAuthenticator = async () => {
await this.driver.getSession().then(async session => {
this.driver
.getExecutor()
.defineCommand('AddVirtualAuthenticator', 'POST', `/session/${session.id_}/webauthn/authenticator`);
let addVirtualAuthCommand = new Command('AddVirtualAuthenticator');
addVirtualAuthCommand.setParameter('protocol', 'ctap2');
addVirtualAuthCommand.setParameter('transport', 'internal');
addVirtualAuthCommand.setParameter('hasResidentKey', true);
addVirtualAuthCommand.setParameter('isUserConsenting', true);
await this.driver.getExecutor().execute(addVirtualAuthCommand);
});
};
Note that this.driver is of type WebDriver.
Call addVirtualAuthenticator before hitting any code that interacts with navigator (in our case user registration involved a call to navigator.credentials.create). If you need access to the publicKey, i.e. via navigator.credentials.get({ publicKey: options }) during login, then hasResidentKey is critical.
A good resource for an example if you're implementing this in java and using selenium 4 is the tests on selenium itself. You basically need to
Create a virtual authenticator
In your case, you should set the transport to internal and hasUserVerification to true to simulate touchID.
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions();
options.setTransport(Transport.INTERNAL)
.hasUserVerification(true)
.isUserVerified(true);
VirtualAuthenticator authenticator =
((HasVirtualAuthenticator) driver).addVirtualAuthenticator(options);
Perform the action that triggers registration.
If everything goes right, the browser should not show a dialog. Instead, it should immediately return a credential.
For any other language or selenium version, you will need to drop into calling the WebDriver protocol directly. As you pointed out, the W3C spec has documentation on the protocol endpoints.
For java, it might be something like
browser.driver.getExecutor().defineCommand(
"AddVirtualAuthenticator", "POST", "/session/:sessionId/webauthn/authenticator");
// ...
Command addVirtualAuthCommand = new Command("AddVirtualAuthenticator");
addVirtualAuthCommand.setParameter("protocol", "ctap2");
addVirtualAuthCommand.setParameter("transport", "usb");
browser.driver.getExecutor().execute(addVirtualAuthCommand);
For javascript, you should be able to use defineCommand and webDriver.execute in a similar fashion.
This is worst practice in selenium
Two Factor Authentication shortly know as 2FA is a authorization
mechanism where One Time Password(OTP) is generated using
“Authenticator” mobile apps such as “Google Authenticator”, “Microsoft
Authenticator” etc., or by SMS, e-mail to authenticate. Automating
this seamlessly and consistently is a big challenge in Selenium. There
are some ways to automate this process. But that will be another layer
on top of our Selenium tests and not secured as well. So, you can
avoid automating 2FA.
There are few options to get around 2FA checks:
1.Disable 2FA for certain Users in the test environment, so that you can use those user credentials in the automation.
2.Disable 2FA in your test environment.
3.Disable 2FA if you login from certain IPs. That way we can configure our test machine IPs to avoid this.
Related
I'm having an issue getting Login Kit to work. Similar to the question asked here I have the correct redirect domain listed in tiktok settings and the redirect_uri is basically just "domain/tiktok" but no matter what I do I get the same error message:
Below is my backend code - it's basically exactly the same as what is listed in the tiktok docs. Any help on this would be much appreciated!
const CLIENT_KEY = 'my_key'
const DOMAIN = 'dev.mydomain.com'
const csrfState = Math.random().toString(36).substring(2);
res.cookie('csrfState', csrfState, { maxAge: 60000 });
const redirect = encodeURIComponent(`https://${DOMAIN}/tiktok`)
let url = 'https://www.tiktok.com/auth/authorize/';
url += '?client_key=' + CLIENT_KEY;
url += '&scope=user.info.basic,video.list';
url += '&response_type=code';
url += '&redirect_uri=' + redirect;
url += '&state=' + csrfState;
res.redirect(url);
UPDATE 8/13/2022
I submitted the app for review and was approved so the status is now "Live in production" instead of "staging". The issue is still there - still showing error message no matter what domain / callback URL I use
UPDATE 8/16/2022
OK so I've made some progress on this.
First off - I was able to get the authentication/login screen to finally show up. I realized to do this you need to:
Make sure that the status of your app is "Live in production" and not "Staging". Even though when you create a new app you may see client_key and client_secret show up don't let that fool you - Login Kit WILL NOT WORK unless your app is submitted and approved
The redirect_uri you include in your server flow must match EXACTLY to whatever value you entered in "Registered domains" in the Settings page. So if you entered "dev.mydomain.com" in Settings then redirect_uri can only be "dev.mydomain.com" not "dev.mydomain.com/tiktok".
I think I might know what the issue is. My guess is that before - on the Settings page you had to enter the FULL redirect URL (not just the domain) and whatever redirect uri was included in the authorization query was checked against this value which was saved in TikTok's database (whatever was entered in the Settings page when path/protocol were allowed). At some point recently, the front-end business logic was changed such that you could only enter a domain (e.g., mydomain.com) on the Settings page without any protocols - however TikTok's backend logic was never updated so during the Login flow they are still checking against an EXACT match for whatever was saved in their DB as the redirect uri - this would explain why an app that was previously using the API with a redirect uri that DOES include protocols (e.g., for Later.com their redirect uri is https://app.later.com/users/auth/tiktok/callback) continues to work and why for any app attempting to save redirect WITH protocols are getting the error message screen. My gut feeling is telling me that the error is not on my part and this is actually a bug on TikTok's API - my guess is it can be addressed either by changing the front-end on the Settings page to allow for path/protocols (I think this is the ideal approach) or to change their backend so that any redirect uri is checked such that it must include 1 of the listed redirect domains.
I've been emailing with the TikTok team - their email is tiktokplatform#tiktok.com - and proposed the two solutions I mentioned above. I suggest if you're having the same issue you email them as well and maybe even link this StackOverflow question so that maybe it will get higher priority if enough people message them about it.
If you're looking for a shot-term hack I'd recommend creating a dedicated app on AWS or Heroku with a clean domain (e.g., https://mydomain-tiktok.herokuapp.com) and then redirect to either your dev or production environment by appending a prefix to the "state" query (e.g., "dev_[STATE_ID]"). I'll just reiterate I consider this a very "hacky" approach handling callbacks and would definitely not want to use something like this in production.
In my case, the integration worked after doing following steps:
In TikTok developers page:
Like #eugene-blinn said: make sure your app is in Live in production status (I couldn't find anything in the documentation about why Staging apps don't work);
Add the Login Kit product to your app and set the Redirect domain field with your host domain, for example: mywebsite.com.
In your code:
From my tests, I could add whanever url path I wanted, the only constraint was that the domain should match with step 2. So, yes, you can add https://mywebsite.com/whatever/path/you/want in redirect_url parameter.
That's it. It should work with these 3 steps.
Additionally, I got other issue related to use specific features in the scope property (like upload or read videos, etc), so here the solution as well:
Only add Video Kit product to the TikTok app and set video.upload or video.list in the scope authorize request won't work unless you also add the TikTok API product in your TikTok app as well. Btw, it neeeds to be approved too.
TikTok fixed the bug that resulted in URL mismatch with redirect domain from working. However, they fixed it only for paths (e.g., /auth/tiktok) but PORT additions still result in an error - so www.domain.com:8080/auth/tiktok won't work but www.domain.com/auth/tiktok WILL work
UPDATE 10/3/2022
Got the following response directly from TikTok engineering team:
At this point, we only support production integrations with TikTok for Developers and require that you have a URL without port number. However, we understand from your communication that this makes it harder for you to build, test, and iterate your integration with us. Unfortunately, at this time, we do not have a timeline for when this additional support for development servers will be added. We request that you only redirect to URLs without port numbers. Thank you for the feedback.
The frontend of the developer's dashboard still rejects protocol and path in validation. However, the backend skips the path validation.
To be able to update the "Redirect domain" simply:
Open dev tools in chrome and go to the "Network" tab.
Clic on "Save changes" button on the dashboard.
Right clic on the "publish" request that appeared and copy as cURL.
Modify the "redirect_domains" field in the request before pasting it in the terminal.
I believe the app still needs to be approved and in production to get it to work. I'm still waiting for approval and it has been a couple of weeks.
UPDATE 9/17/2022
Just like #mauricio-ribeiro, my app worked after it was approved to production. Setting up the redirect domain without path and scheme works just fine.
I had the same problem, my solution:
1.- In my TikTok App dashboard, the “redirect_uri” is: mydomain.com, without http/https and without path (/my-redirect-url). Also you can add subdomains using this rule
2.- In my code, I have to add http or https to the redirect_uri, and feel free to use path (/my-redirect-uri)
I hope this help you
I am using https://www.npmjs.com/package/react-native-disable-battery-optimizations-android package for my React Native Android app. This package provides OpenBatteryModal() method to raise popup to get ignore battery optimization permission. But it does not provide how to get permission granted status. So, my question is "Is there any way to get to know what option is selected in permission popup or is there any other workaround in this package to get the grant status? I am struck on this for days. There is not much help also apart from using this package.
It looks like this library (RNDisableBatteryOptimizationsAndroid) has a method to check the status of battery optimization, it's even mentioned on their README under usage. I'm not sure what you've tried, but this is how you can check if the permission was granted using their isBatteryOptimizationEnabled API:
import RNDisableBatteryOptimizationsAndroid from 'react-native-disable-battery-optimizations-android';
RNDisableBatteryOptimizationsAndroid.isBatteryOptimizationEnabled().then((isEnabled)=>{
if(isEnabled){
RNDisableBatteryOptimizationsAndroid.openBatteryModal();
}
});
You can't get the user selected option directly, so in case the above example is not enough - you can write some logic to get the status when your app becomes active (using react-native AppState) after the modal is closed; since the modal is a new activity you should be notified when your activity becomes active again. Optimize it so you only check the status if openBatteryModal was requested.
I'm using a Javascript SPA to return a query from Microsoft Graph through the Azure AD application, and it works just fine!
The problem is when I try to loggout from the application, it says I was successfully logged out but if try to log in with another user, it logs into the previous one, in this case, me, without even asking for password.
I needed that this application could log in just few people in my organization, but anyone with "#example.com" can access my application, without the need to be signed to it or not.
I've already cleared the browser's cache and cookies and it doesn't work. Already configured the app to store the cache in the session but it also failed.
The code I'm using is available in:
https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-javascript-spa
The only differences are that I'm using another querys instead the "https://graph.microsoft.com/v1.0/me" and the permissions needed to get them.
I just needed a way to choose specific people to log into the application instead of all the organization and to fix this logout problem.
If I understood you well, I believe your solution is the option select_account.
Here is a code snippet to illustrate:
const clientApplication = new Msal.UserAgentApplication(config);
const loginRequest = {
scopes: [config.webApiScope],
prompt: "select_account",
}
clientApplication.loginPopup(loginRequest).then(function (loginResponse) {
//your code
});
I'm wondering if it's possible to, say, open up a jsfiddle on a random computer and log in and authenticate and use the drive API, without having to have a local server running all the time? And how exactly does one go about setting this up? I'm sorry if this is a simple question but I'm just sorta lost because the instructions that I've found so far are unclear.
Edit:
So far I have, following from here and here:
Created a project via the Google Developers Console.
Opened up that project there, navigated to APIs under APIs & auth, and ensured that Drive API was enabled
Went into credentials, and clicked on "Create a new Client ID"
Selected "Web Application"
Set authorized javascript origins to http://localhost:4567
Deleted any contents in Redirect URI's and left it blank, then pressed "Create Client ID."
Took a sample like this, this, or this, and stored it in a local file named named index.html.
This wouldn't run by simply opening in a brower, so I had to host a local server
I navigated into that directory in the command line and then typed "python -m SimpleHTTPServer 4567" (without the quotes) and this hosted a local server
Opened http://localhost:4567 in my web brower, and all of these samples work fine, after copying the newly created client ID into these files where they ask for it.
I also have made a python application, to use pydrive I:
Clicked on "Create a new Client ID", then "Installed Application" and "Other", then "Create Client ID."
Next I went to the Old Google APIs Console, clicked on API Access, found that client ID, and clicked Download JSON.
I placed this client_secrets.json next to my python application, and this allowed pydrive to authenticate successfully and I could access and modify my google drive files anywhere using that client ID. Though of course I deleted this client_secrets.json before giving the application to another person, and showed them how to do this process as well.
However, beyond this, I'm a little unsure about, specifically:
How one can use the drive api in web applications without having to set up a local server, say simply by running code in jsfiddle and having requests sent through my project via using a Client ID, and
If a local server is set up that can be accessed by anyone on the web, how one can modify the above steps to allow any client to open that server's webpage to use the google drive API?
I know that I most likely need to set up a Public API access in the developers console, but am not entirely sure what Referers I should use as well. So is there a simple way to do any of this?
I also know that gspread can open google excell spreadsheets only using the client's username and password, so I'm suspecting that what I'm looking for is possible, but I'm not sure.
Okay, so I found a solution that works pretty well:
Make an OAuth.io account (It's free).
Once you're logged in, go to your oauth.io dashboard.
You should be looking at a Default App, or you can make another one it doesn't really matter.
Under "Domains & URLs whitelist", you'll see a little box that says "localhost." Type in "
http://fiddle.jshell.net/" (without the quotes) and press enter. This allows any jsfiddle to authenticate with your application
Next, we need to enable Google Drive support
Go to the Google Developers Console
Like before, navigate to Credentials under APIs and Auth.
Click "Create new Client ID"
Select Web Application
Under "Redirect URIs" put "https://oauth.io/auth" and "http://oauth.io/auth" (without quotes, on separate lines)
Under "Javascript Origins" put "https://oauth.io" and "http://oauth.io"
Press "Create Client ID"
Now go to your Oauth.io key manager. Click on Google Drive in the list to the left. Press next, and enter your client_id and client_secret that you created in the previous step (via the Google Developers Console)
Select online (at least online is preferred since offline should be for server side only)
Click on the Scope text book, and select "https://www.googleapis.com/auth/drive (View and manage the files in your google Drive)."
Press finish. Now click on the Try Auth button, and hopefully you should get some output like
{
"access_token": "ya29.AwH-1N_gnstLBuZfOR4W9CCcggKrQpMyKYV4QVEtCiIzHozNU5AfUJoYQzukALfjdiw2iOCUve7JbQ",
"token_type": "Bearer",
"expires_in": 3600,
"provider": "google_drive"
}
To use this, you can do something like:
// This only works because we're set to "No wrap - in <head>"
function ShowDriveFileList() {
var accessToken;
// Initialize OAuth with our key
OAuth.initialize('lmGlb8BaftfF4Y5en_c8XYOSq2A');
// Connect to google drive, and print out file list
OAuth.popup('google_drive').done(function (result) {
var xmlHttp = null;
xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "https://www.googleapis.com/drive/v2/files", false);
xmlHttp.setRequestHeader("Authorization", "Bearer " + result.access_token);
xmlHttp.send(null);
alert(xmlHttp.responseText);
}).fail(function (err) {
alert(err);
});;
}
Which you can find at http://jsfiddle.net/JMTVz/41/. This uses my oauth.io client id, but you can replace it with yours and it should work as well.
You can do the same thing using the OAuth Playground.
See How do I authorise an app (web or installed) without user intervention? (canonical ?)
Step 11 will be different. Instead of pasting the refresh token into your server app (which you don't have), you'll paste the access token into your JS in the same way as you are doing in your answer.
I'm creating an HTML autorun. There is no restriction in using javascript as it will be run from XULRunner. I want a way to detect if internet connection exist or not. This doesn't work for me
$(document).ready(function() {
var online = navigator.onLine;
// a workaround for a flaw in the demo system (http://dev.jqueryui.com/ticket/4375), ignore!
function doit() {
if (navigator.onLine(connected)){
alert("YES!");
} else {
alert("NO!");
}
}
Is there a better way?
Update: Came to know that the code above only detects the browser state and not if internet is available. For me the contact form in the autorun has to check if internet is connected and alert the user.
Use an XMLHttpRequest in javascript to request a small file from your server. If the request returns an error or times out, then the site is probably unreachable. If you don't have a particular webserver to test on, you could use something with a high degree of reliability, like the Google server.
Though, if you do use the Google server that wouldn't necessarily correspond to your own site being reachable, it would just mean that you are able to connect to the internet. Your own site may down\otherwise unavailable.