Authentication is a common way to handle security for all applications. This is how the application can answer the question: “Who you are?” There exists many different combinations of methods and technologies used to authenticate a user. For this reason, each application that is recorded with Clone Environment could require a unique solution to bypass the unauthenticated state.
You might have an authentication issue if you notice the following behaviors:
- On preview, the demo is redirected to a login screen.
- The demo partially renders but doesn't seem to load completely.
- There is an infinite loading spinner or a blank screen.
If any of the above are observed, we have to use both the demo and the real application to investigate what the cloned app is missing in order to move forward. The following sections will cover some troubleshooting steps and how to resolve them.
Missing Web objects
In the real application, it's possible that an authentication token is stored in either the browsers local, session, and/or cookies storage. Follow the instructions below to help you find an auth token:
- From the real app, open Chrome Dev Tools
- Navigate to the Application tab
- Under the Storage section, click on the triangles next to Local, Session and Cookies to open those sub menus.
- Starting with local storage, click on the application's domain to reveal a table of keys and their corresponding values
- Search the list of storage items for any keys that contain "auth", "user", "session", "jwt", "token"
- Repeat the previous step for session and cookies storage
- Preview the replay, open Dev Tools, and compare the storage items found in the replay vs the real app. Are any items missing? Do some keys match but have different values?
If you find an auth related key in the the real app, and it is missing from the replay, we can try adding the storage item and checking if this resolves our issue. Let's take this example, where after recording the application's dashboard, we see the login screen in the preview. After following the steps above, we can see that there is a cookie named "TruckTapAuth" that is very likely an item that the front end needs to authenticate the user.
We can test our theory by navigating to the replays Configure page, and creating a New Object under the Cookies tab. The key name will be the title of the object and the value can be copy/pasted from Dev Tools into the Value field.
Preview the replay and confirm that you have now bypassed the login screen.
oAuth Pattern
OAuth is an open authorization standard that apps can use to provide client applications with “secure delegated access.” OAuth works over HTTPS and authorizes devices, APIs, servers, and applications with access tokens rather than credentials.
The first clue that you're dealing with an app using OAuth, is a failed GET request to the path /authorize. This request will look very similar to the image below:
In this case, the request to /authorize is attempting to reach the real application due to the code that initiates it. In order for the request to target the replay, we need to use a HTML Environment Backend snippet to intercept the request for the JavaScript file that contains this code. To find which request you need to intercept, use the following instructions:
- Open Chrome Dev Tools and navigate to the Network tab
- Reload the page
- Use Cmd+F to open the Search bar and enter
this.domainUrl=( - Click on the result and scroll down to the Request Headers section within the Headers tab. Copy the value of the
:path - Using the code snippet below, replace the contents of the string on line 2 with the path for the request
- With the Request still selected, navigate to the Preview tab and click into the code. Use Cmd+F to search for the text:
this.domainUrl=( - In line 6 of the snippet below, replace the variable
iwith the variable found in the code within the Preview tab.
replay_backend.get('/main.fed7038996cc02c9bb1c.bundle.js') // Path for JS request.post_process(async (response) => {let js_content = await response.text();js_content = js_content.replace(`this.domainUrl=(i=this.options.domain`, // Code within JS file`this.domainUrl=(i="${self.origin}"`);return js_content;});
Below is the complete HTML Environment Backend snippet needed to resolve the authentication issue for our example app:
let nonce = null;replay_backend.get('/main.fed7038996cc02c9bb1c.bundle.js').post_process(async (response) => {let js_content = await response.text();js_content = js_content.replace(`this.domainUrl=(i=this.options.domain`,`this.domainUrl=(i="${self.origin}"`);return js_content;});replay_backend.get('/authorize').handle(async (request) => {const url_params = new URLSearchParams(new URL(request.url).search);const state = url_params.get('state');nonce = url_params.get('nonce'); // save this for /oauth/token requestreturn new Blob([`<!DOCTYPE html><html><head><title>Authorization Response</title></head><body><script type="text/javascript">(function(window, document) {var targetOrigin = "*";var webMessageRequest = {};var authorizationResponse = {type: "authorization_response",response: {"code":"vBslnXCoOlyb0t0MUmq6fnbKC0ZB4HAibKSsB1Kfey9Z2","state":"${state}"}};var mainWin = (window.opener) ? window.opener : window.parent;if (webMessageRequest["web_message_uri"] && webMessageRequest["web_message_target"]) {window.addEventListener("message", function(evt) {if (evt.origin != targetOrigin)return;switch (evt.data.type) {case "relay_response":var messageTargetWindow = evt.source.frames[webMessageRequest["web_message_target"]];if (messageTargetWindow) {messageTargetWindow.postMessage(authorizationResponse, webMessageRequest["web_message_uri"]);window.close();}break;}});mainWin.postMessage({type: "relay_request"}, targetOrigin);} else {mainWin.postMessage(authorizationResponse, targetOrigin);}})(this, this.document);</script></body></html>`,],{ type: 'text/html' });});replay_backend.post('/oauth/token').handle(async () => {const url = self.origin + '/';const token_start ='eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImptMTJpMmRxWXhSNUI4UE1WbWZmdyJ9.';const access_data = replay_utils.base64_encode(JSON.stringify({'https://metadata.io/sessionId': 'VzA6tRV9o7VpURWFj01lH1zrYvaqRePw','https://metadata.io/impersonationAccountId': '-1','https://metadata.io/loginsCount': 6,iss: url,sub: 'auth0|5487',aud: [url],iat: Date.now() - 1000,exp: Date.now() + 60 * 60 * 1000,azp: 'YClAyP2VLyDxtkCerVBuPvQFMNnqJjGD',scope: 'openid profile email offline_access',}));const id_data = replay_utils.base64_encode(JSON.stringify({given_name: 'George',family_name: 'Costanza',nickname: 'george.costanza',name: 'george.costanza@vandelay.com',picture:'https://s.gravatar.com/avatar/67bcc3e0bfea5d8287474281fe9ca02d?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fjo.png',updated_at: '2022-06-07T03:29:04.518Z',email: 'george.costanza@vandelay.com',email_verified: true,iss: url,sub: 'auth0|5487',aud: 'YClAyP2VLyDxtkCerVBuPvQFMNnqJjGD',iat: Date.now() - 1000,exp: Date.now() + 60 * 60 * 1000,nonce: nonce,}));return JSON.stringify({access_token: token_start + access_data + '.fake',id_token: token_start + id_data + '.fake',scope: 'openid profile email',expires_in: 86400,token_type: 'Bearer',});});


