How to login with Microsoft SSO in Cypress test when getting cookies blocked error - azure-active-directory

I'm trying to login to microsoft SSO account in my cypress test and I'm getting "Your browser is currently set to block cookies." error. I am using Cypress version 10+. I've seen posts about using Cypress.Cookies.preserveOnce() but it is deprecated in the 10+ version. This is the error I'm seeing in my cypress test:
This is how I'm trying to login.
cy.session(args, () => {
cy.origin(`https://login.microsoftonline.com/`, {args}, ({ email, password}) => {
cy.visit('/')
cy.get('[name="loginfmt"]').type(email);
cy.get('[name="passwd"]').type(password);
cy.get('[type="submit"]').type('{enter}');
})
});
Any help will be appreciated!

// cypress.config.js
experimentalSessionAndOrigin: true,
pageLoadTimeout: 180000,
experimentalModifyObstructiveThirdPartyCode: true,
chromeWebSecurity: false,
experimentalInteractiveRunEvents: true,

// commands.js
Cypress.Commands.add("jwtlogin", (name, password, token, path) => {
const args = {
name,
password,
token,
path
}
cy.visit(Cypress.config('apiLoginURL'))
cy.on('uncaught:exception', (err, runnable) => {
return false
})
cy.wait(7000)
cy.get('.microsoft').click()
cy.origin('https://login.microsoftonline.com', {
args
}, ({
name,
password,
token,
path
}) => {
cy.wait(17000)
cy.get('body').then((body) => {
if (body.find('#otherTileText').length > 0) {
cy.contains('Use another account').click()
cy.get('#i0116').type(name)
} else {
cy.get('#i0116').type(name)
}
})
cy.get('#idSIButton9').click()
cy.wait(3000)
cy.get('#i0118').type(password)
cy.contains('Sign in').click()
cy.get('div.tile:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2)').click()
cy.task("generateOTP", token).then(token => {
cy.get("#idTxtBx_SAOTCC_OTC").type(token);
cy.get('#idSubmit_SAOTCC_Continue').click()
cy.wait(3000)
})
cy.on('uncaught:exception', (err, runnable) => {
return false
})
})
cy.wait(5000)
cy.get('body').then((body1) => {
cy.wait(5000)
if (body1.find('#idBtn_Back').is(":visible")) {
cy.wait(5000)
cy.get('#idBtn_Back').click()
}
cy.wait(16000)
})
})
//hooks file
before(() => {
cy.origin('https://login.microsoftonline.com', () => {
cy.visit('https://login.windows.net/common/oauth2/logout')
})
cy.on('uncaught:exception', (err, runnable) => {
return false
})
cy.jwtlogin('testaccount.com', 'Derersr1', 'dfdfd', 'jeet.txt')
cy.window().then(win => win.sessionStorage.clear());
cy.clearCookies();
cy.clearLocalStorage()
cy.wait(11000)
})

Sometimes when logging into Microsoft SSO in Cypress, the error message that appears is 'Your browser is currently set to block cookies,' even in cases where the actual reason for the error is something else. For instance, my account has been suspended and locked. In Cypress, the error message shows "Your browser is currently set to block cookies," but in my system's browser, it shows a different message.
This can be confusing, so it's important to keep in mind that the error message may not always accurately reflect the issue at hand.

Related

What is the correct way to call updateCachedData on a click event in a component that uses the RTKQ query?

I can only think of storing a reference to updateCachedData somewhere globally and use it in that click event but I am not sure this is the React way of doing this.
I have a notifications feed built with a Socket.IO server.
By clicking on a notification it should get deleted from the list. (The list should show only unread notifications.)
But when deleting from the list I create a new array as state in the notifications pane.
When I receive a new notification, all the deleted notifications return back - this is not what I intended.
How can I change the cache entry, more precisely remove items from it without remaking the request for all the notifications?
There are no error messages.
Code
getNotifications: build.query<
IDbNotification[],
IGetNotificationsQueryParams
>({
query: (params: IGetNotificationsQueryParams) => ({
url: `notifications?authToken=${params.authToken || ""}&limit=${
params.limit
}&userId=${params.userId || ""}${
params.justUnread ? "&justUnread" : ""
}`,
method: "GET"
}),
keepUnusedDataFor: 0,
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
const { myIo, connectHandler } = getWebSocketConnection(
"notifications",
clone({
subscribtions: arg.userId
? getFollowedUserIds().concat({
uid: arg.userId,
justUnread: arg.justUnread
})
: getFollowedUserIds()
})
);
const listener = (eventData: IDbNotification) => {
if (
(eventData as any).subscriber === arg.userId &&
(!arg.justUnread || typeof eventData.readDateTime === "undefined")
) {
updateCachedData(draft => {
draft.unshift(eventData);
if (draft.length > arg.limit) {
draft.pop();
}
});
}
};
try {
await cacheDataLoaded;
myIo.on("notifications", listener);
} catch {}
await cacheEntryRemoved;
myIo.off("notifications", listener);
myIo.off("connect", connectHandler);
}
})
You can use updateQueryData - updateCachedData is just a shortcut for the current cache entry for convenience.
dispatch(
api.util.updateQueryData('getNotifications', arg, (draft) => {
// change it here
})
)
See this for more context: https://redux-toolkit.js.org/rtk-query/usage/optimistic-updates

How to reset recaptcha when using react-redux-firebase

I am working with React-Redux-Firebase. I implemented signing in with phone number. Now I am trying to implement error handling. When number is invalid I display window alert with error message. The only thing left to do is to reset recaptcha. Without it, I am getting error:
reCAPTCHA has already been rendered in this element
I was trying to do according to Firebase documentation
grecaptcha.reset(window.recaptchaWidgetId);
// Or, if you haven't stored the widget ID:
window.recaptchaVerifier.render().then(function(widgetId) {
grecaptcha.reset(widgetId);
}
but it does not work in my code. I dont have grecaptcha implemented. I tried to add it with react-grecaptcha, but it did not work.
Could someone give me a hint how to reset recaptcha after each error, please?
state = {
phone: "",
confirmationResult: {},
};
handleClick = () => {
const recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
"sign-in-button",
{
size: "invisible",
}
);
firebase
.signInWithPhoneNumber(`+${this.state.phone}`, recaptchaVerifier)
.then((confirmationResult) => {
this.setState({ confirmationResult });
})
.catch((error) => {
// Error; SMS not sent
// Handle Errors Here
window.alert(`${error.code}, ${error.message}`);
recaptchaVerifier.reset(); // How can I do that?
});
};
I've been struggling with this problem for several days, maybe my answer will help someone.
export const requestRecaptchVerifier = () => {
window.recaptchaVerifier = new RecaptchaVerifier(
"recapcha-container",
{
size: "invisible",
},
auth
);
};
I then call signInWithPhone from another function and handle the error like this:
await signInWithPhone(formik.values.phone)
.then(() => {
// ... my code
})
.catch(() => {
window.recaptchaVerifier.recaptcha.reset();
window.recaptchaVerifier.clear();
});
All the difference in
window.recaptchaVerifier.recaptcha.reset()
And
window.recaptchaVerifier.clear()
I'm no expert but from the documentation and by talking with you in the comment section I think you need to pass a callback. Like this:
const recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': function(response) {
// reCAPTCHA solved, allow signInWithPhoneNumber.
firebase
.signInWithPhoneNumber(`+${this.state.phone}`, recaptchaVerifier)
.then((confirmationResult) => {
this.setState({ confirmationResult });
})
.catch((error) => {
// Error; SMS not sent
// Handle Errors Here
window.alert(`${error.code}, ${error.message}`);
recaptchaVerifier.reset();
});
}
});
Reference: https://firebase.google.com/docs/auth/web/phone-auth#use-invisible-recaptcha
Hope this helps!

I am trying to preserve the Session cookie through multiple tests in cypress but Cypress.Cookie.preserveOnce() is not working

Am I implementing this functionality wrong or am i missing something?? I watch the application tab of the developer tools, the cookie appears after the setToken() method but despite the Cypress.Cookies.preserveOnce('token_name') in the before each hook. The token is still erased in between tests.
before(() => {
cy.setToken()
})
beforeEach(() => {
Cypress.Cookies.preserveOnce("token_name")
cy.visit(urlPrefix + 'applications').wait(beforeEachWait)
})
after(() => {
cy.clearCookies()
})
it('Form should have title', () => {
cy.contains('Edit Application').should('be.visible')
})
it('The Save button should be disabled until changes have been made', () => {
cy.get('[data-cy=saveBtn]').should('be.disabled')
cy.get('[data-cy=applicationName] input').type(' edited')
cy.get('[data-cy=saveBtn]').should('be.enabled')
})
it('The cancel button redirects to the list view', () => {
cy.get('[data-cy=cancelBtn]')
.click()
.wait(500)
cy.url().then(url => {
const applicationsTitle = url.split('#/')[1]
expect(applicationsTitle).to.equal('applications')
})
})
it('should have delete button in edit mode', () => {
cy.get('[data-cy=deleteBtn]').should('be.visible')
})
})
use this one
Cypress.Cookies.defaults({
preserve: 'session_id',
})
it will work or
before("cookies",() => {
cy.setCookie('session_name', 'value')
}
cypress latest version 10.8.0 no need to add setCookie. Add expermentalSessionAndOrigin and chromeWebSecurity in cypress.config.ts file
e2e: {
baseUrl: "xxxxxxxxxxxxxxxxxxx",
experimentalSessionAndOrigin: true,
chromeWebSecurity: false,
}

Prevent Facebook login from remounting the app

There seems to be an issue with my implementation of the Facebook login for react-native
When I click on "Log in with the Facebook app" it opens the facebook app, then when I click "Continue" it reopens my app but it performs a remount. Part of my logic is to open another modal after that for some extra information from the user but calling this.setState() in the callback from the facebook login gives a `Can't call setState (or forceUpdate) on an unmounted component. And I can't seem to figure out how to get around this.
facebookLogin = async () => {
let result;
try {
this.setState({
loggingInWithFacebook: true
})
LoginManager.setLoginBehavior('native');
result = await LoginManager.logInWithReadPermissions(['public_profile', 'email']);
} catch (nativeError) {
this.setState({
loggingInWithFacebook: false
})
this.props.dispatch(openSnackBar({
message: "There was an error opening facebook to login"
}))
}
if (result.isCancelled) {
this.setState({
loggingInWithFacebook: false
})
} else {
this.FBGraphRequest('first_name, last_name, id, email', this.FBLoginCallback);
}
}
FBGraphRequest = async (fields, callback) => {
const accessData = await AccessToken.getCurrentAccessToken();
const infoRequest = new GraphRequest('/me', {
accessToken: accessData.accessToken,
parameters: {
fields: {
string: fields
}
}
}, callback);
new GraphRequestManager().addRequest(infoRequest).start();
}
FBLoginCallback = async (error, result) => {
if (error) {
this.setState({
loggingInWithFacebook: false
});
} else {
const {id, picture, ...rest} = result
this._handleSocialLogin({...rest, facebook: id})
}
}
_handleSocialLogin = (data) => {
this.props.dispatch(socialLogin(data)).then(res => {
this.props.dispatch(getGroups()).then(res => {
this.setState({
loggingInWithFacebook: false,
loggingInWithGoogle: false
})
AsyncStorage.getItem('onBoarded').then((res)=> {
if (res === 'true') {
this.props.navigation.navigate("Map")
} else {
this.props.navigation.navigate("OnBoarding")
}
})
}).catch(err => console.log(err.response.body))
}).catch(err => {
if (err.err === 'not found') {
this.setState({
enterPhoneModalVisible: true,
socialData: data
})
} else {
this.props.dispatch(
openSnackBar({
message: "There was an error logging in, please try again"
})
);
this.setState({
loggingInWithFacebook: false,
loggingInWithGoogle: false
})
}
})
}
EDIT:
My Appdelegate looks like this
#import <FBSDKCoreKit/FBSDKCoreKit.h>
In my didFinishLaunchingWithOptions
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
BOOL handledFB = [[FBSDKApplicationDelegate sharedInstance] application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation
];
BOOL handledRCT = [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
return handledFB || handledRCT;
EDIT 2:
I seem to have gotten it to work by changing my openUrl to the following
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [[FBSDKApplicationDelegate sharedInstance] application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation
] || [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}
Any idea why this works?
EDIT 3:
I'm still getting a Login Failed error randomly and there doesn't seem to be any definitive way to cause this to happen it just happens sometimes and then trying to login again right after it usually goes through

Public API call working on Mozilla but not on Chrome (react-app)

I recently tried to deploy my first react-app on to the web. The website is about looking up details for a certain pokemon and making your own card if you like.
I use Mozilla as my main browser and everything works pretty good. But when I ask for a pokemon request (GET) on chrome I don't get any results. If I have a look at the network console I get a 301 Error (from disk cache). What does this mean? You can look at my website at:
https://daan.boschmans.mtantwerp.eu/
I deployed my app using the npm run build command.
I added the .htaccess file in the public folder with the proper lines.
GET REQUEST:
export const getPokemonSprites = (name) => {
return fetch(`https://pokeapi.co/api/v2/pokemon-form/${name}`).then((response) => {
if(response.statusText === 'OK') {
return response.json();
}
throw new Error('Network response was not ok.');
})
}
export const getPokemonMoves = (name) => {
return fetch(`https://pokeapi.co/api/v2/pokemon/${name}`).then((response) => {
if(response.statusText === 'OK') {
return response.json();
}
throw new Error('Network response was not ok.');
})
}
This I how I handle the GET call:
getPokeData() {
if (this.state.searchValue && this.state.name !== this.state.searchValue) {
this.setState({ isLoading: true, hasError: false, name: "", sprites: [], moves: [], height: "", weight:"", specials: [], base_experience: "", type: [], stats:[], items: [], });
Promise.all([ getPokemonSprites(this.state.searchValue),getPokemonMoves(this.state.searchValue)])
.then( ([spriteList, pokeDetails]) => {
const sprites = Object.values(spriteList.sprites);
const moves = Object.entries(pokeDetails.moves);
const abilities = Object.entries(pokeDetails.abilities);
const types = Object.entries(pokeDetails.types);
const stats = Object.entries(pokeDetails.stats);
for (const [ , value] of Object.entries(moves)) {
this.state.moves.push(value[1].move.name);
}
for (const [, value] of Object.entries(types)) {
this.state.type.push(value[1].type.name);
}
for (const [, value] of Object.entries(abilities)) {
this.state.specials.push(value[1].ability.name);
}
for (const [, value] of Object.entries(stats)) {
let statsValue = `${value[1].stat.name}: ${value[1].base_stat}`;
this.state.stats.push(statsValue);
}
this.setState({sprites, name: spriteList.name, height: pokeDetails.height, weight: pokeDetails.weight, base_experience: pokeDetails.base_experience })
}).then(() => { this.setState({isLoading: false, searchValue: ""})})
.catch(() => { this.setState({isLoading: false, searchValue: "", hasError: true}) })
}
}
Any tips would be really appreciated
Thanks
Firstly, nice site. Looks like a fun little project.
I tried the website on Chrome and it works fine for me.
Looks as though you have a service worker that is caching content. If you used create-react-app, it comes with a service worker built it and looks as though it is caching the content of your API calls in your browser.
I suspect there is an issue with your Chrome's cache. You could try clearing the cache by following this suggestion here or alternatively it may be worth trying to reinstall chrome.

Resources