`useAuthRequest` always returns `dismiss` - react-native

SDK Version: 37
Platforms(Android/iOS/web/all): Android (Expo Client)
I’m using useAuthRequest from expo-auth-session to fetch an authentication code for itsme, a Belgian OpenID Connect authentication provider. I'm using the Expo client on Android.
The code looks like this:
export default function SignUpScreen() {
const discovery = useAutoDiscovery('https://e2emerchant.itsme.be/oidc');
const [request, response, promptAsync] = useAuthRequest(
{
clientId: Itsme.CLIENT_ID,
redirectUri: makeRedirectUri(),
scopes: [
'openid',
'service:CURVO_TEST_SHAREDATA',
'profile',
'email',
'phone',
'address'
],
extraParams: {
claims: JSON.stringify({
userinfo: {
'tag:sixdots.be,2016-06:claim_nationality': null,
'tag:sixdots.be,2016-06:claim_city_of_birth': null,
'tag:sixdots.be,2016-06:claim_eid': null,
'tag:sixdots.be,2017-05:claim_photo': null
}
})
}
},
discovery
);
return (
<TouchableOpacity onPress={promptAsync}>Log in</TouchableOpacity>
);
When the “Log in” button is pressed, the following happens:
(OK) The itsme application is correctly opened. I can tell that the scopes have also been correctly communicated.
(OK) I can authenticate within the itsme application with my itsme PIN code.
(OK) The itsme application then closes, which is expected.
(OK) My app is re-opened.
(NOT OK) The response variable becomes equal to { type: 'dismiss' }.
Whatever I do, the response is always “dismiss”. I find it strange because the docs say:
If the authentication is dismissed manually with AuthSession.dismiss() , the result is { type: 'dismiss' } .
However, I never call AuthSession.dismiss().
It looks like it’s failing right at the last step, when it’s supposed to parse the authorization code out of the response URL.
I tried several things:
starting Expo in all three modes: tunnel, lan, localhost
adding a path to the redirect URI, like makeRedirectUri({ path: '/itsme' })
the AppState.currentState “trick” described in a (related?) GitHub issue.
None of these tries worked and I’m out of ideas. Any thoughts or suggestions? Or maybe how to debug inside `expo-auth-session to see what’s going on?
Thank you in advance.

Related

Playwright: unable to login via API setting cookie (able to do it with Cypress)

I'm trying to implemented login via API following Playwright's guidelines but somehow nothing seems to be working.
As a comparison I've built the same in Cypress and it works out of the box:
Context:
Playwright Version: 1.30
Operating System: Mac
Node.js version: v16.19.0
Browser: Chromium
I am unable to make a simple API login that works perfectly using Cypress instead. Let me share the 2 code snippets for comparison:
Simple test case:
API request to the login end-point - Auth token is retrieved
set the auth token as a cookie
navigate to a page that is accessible only if authenticated
Code Snippet
Cypress (working fine)
const body = {
username: 'username...',
password: 'password',
rememberMe: true,
};
describe('Login via API to management console', () => {
it('Login via API to management console', () => {
cy.request({
method: 'POST',
url: loginEndPoint,
headers: {
'Content-Type': 'application/json',
},
body,
}).then((response) => {
cy.setCookie('Authorization', `Token ${response.body.data.token}`);
});
cy.visit(`/management`);
});
});
Playwright (not working)
test('Login via API', async ({ browser }) => {
const context = await browser.newContext();
const page = await context.newPage();
const loginResponse = await context.request.post(`https://${process.env.MANAGEMENT_URL}/web/api/v2.1/users/login`, {
data: {
username: process.env.MANAGEMENT_USER,
password: process.env.MANAGEMENT_PASSWORD,
rememberMe: true,
}
});
const {
data: { token },
} = await loginResponse.body().then((b) => {
return JSON.parse(b.toString());
});
expect(token).toMatch(/^[a-z0-9]{80}$/)
await context.addCookies([{ name: 'Authorization', value: `Token ${token}`, path: '/', domain: `https://${process.env.MANAGEMENT_URL}` }]);
await page.goto(`https://${process.env.MANAGEMENT_URL}/management/`);
await expect(page).toHaveURL(/management/);
});
Describe the bug
Both scripts are successful at retrieving the authentication token but somehow either I'm doing something wrong with setting the cookie in Playwright or there is an issue. I'd assume the 2 scripts should be comparable.
Furthermore: I've tried to execute login via UI using global-setup, saving the storage-state, loading it before running the test and it fails also in this case... so there is something that is not setting properly the state in this case or the cookie in the previous one.
Not entirely sure why the cookie approach wasn’t working, perhaps the https:// part should be removed from the domain?
That being said, in Playwright you shouldn’t even need to do that especially within a single test, looking at the Playwright docs on signing in via the API and related page about the request context particularly under cookie management. The associated request and browser contexts share cookies, so once you complete the login request, the browser should already have the cookie state too and be logged in, so you should be able to just remove getting the token and adding the cookie. Or you can login with the API in the global setup even, as that doc showed. Just make sure in that case to save the storage state, and specify the same file in your config.
I see you tried the global setup approach (through the UI, but you can use the API since you have it), not sure what happened there. I would say to ensure that you specified the storageState in the config; I would be curious how you loaded it as mentioned, and if you’re still having problems maybe share the code you’re using for that piece?
Hope that helps or we can troubleshoot further!

HTTP requests does not work on my real android device (React Native Expo Tunnel)

I'm facing this issue while posting data to my server via axios.post(). It always catches the error "Network Error" when I run my app on my real android device. But when I use an android emulator device, it works correctly and returns the response. I tried to use "ngrok http 5000" and used the uri that ngrok gave me but that didn't work either. I'm hopeless at this point because my app is using QR code scanner and indie notifications. I'm unable to test it while using emulator. Please help me!
I'm leaving my axios.post() method below
const login = async (email, password, navigation) => {
userState.loading = true;
axios
.post(`http://${localIP}:5000/api/user/login`, {
email,
password,
})
.then((response) => {
userState.loading = false;
userState.user = response.data;
console.log("Data: ", response.data);
navigation.reset({
index: 0,
routes: [{ name: "Home" }],
});
})
.catch((err) => {
userState.loading = false;
console.log("ERR :", err.message);
});
};
I tried to use "ngrok http 5000" and used the uri that ngrok given me but that didn't work either.
Might be a little light on information in order to give a definite answer, it's worth looking at installing a debugger and inspecting the network requests to see where they're going wrong. But here's a couple things to try:
In your AndroidManifest.xml file add the line: <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Use http://my.local.ip:port instead of http://localhost:5000 (looks like you're doing this anyway, if not you get this via ipconfig in Command Prompt)
Try adding "Content-Type": "application/json", Accept: "application/json" headers to your Axios request

Nuxt auth with keycloak: ssr this.$auth.loggedIn always false on page load

I have a setup with nuxt and keycloak as auth strategy which in general is working. I can login via keycloak and then will have this.$auth.loggedIn === true on the page. When navigating via vue-router, this.$auth.loggedIn will also be true when switching to a new page.
But when I then reload the page (CMD+r/F5), server side rendering will have false for this.$auth.loggedIn, while on client side it will be true. This forced me to do a lot of <client-only> blocks in the templates to prevent ssr mismatches.
I wonder if it is possible that on first page load server side rendering can return a page with authorized content? I would think this should be possible since cookies with auth info are set and sent to the server.
Or is that never possible and efficient server side rendering can only be used for non-authorized content?
Versions:
nuxt: 2.15.8
#nuxtjs/auth-next: 5.0.0-1643791578.532b3d6
nuxt.config.js:
auth: {
strategies: {
keycloak: {
scheme: 'oauth2',
endpoints: {
authorization: `${ process.env.KEYCLOAK }/protocol/openid-connect/auth`,
userInfo: `${ process.env.KEYCLOAK }/protocol/openid-connect/userinfo`,
token: `${ process.env.KEYCLOAK }/protocol/openid-connect/token`,
logout: `${ process.env.KEYCLOAK }/protocol/openid-connect/logout`,
},
token: {
property: 'access_token',
type: 'Bearer',
maxAge: 1800,
},
refreshToken: {
property: 'refresh_token',
maxAge: 60 * 60 * 24 * 30,
},
responseType: 'code',
grantType: 'authorization_code',
clientId: process.env.CLIENT_ID,
scope: ['openid', 'profile', 'email', 'roles'],
codeChallengeMethod: 'S256',
redirect: {
logout: '/',
callback: '/',
home: '/',
},
},
},
},
Having a Vue component with this:
created() {
console.log(this.$auth.loggedIn);
},
Will return false for SSR and true on client side on page load/refresh when logged in.
After manually implementing a server side authenticator, I found out that the problem was my local docker setup.
Didn't think this was the problem before, so I forgot to mention it.
I have a local docker container with keycloak and a local docker container with nuxt.
Long story short, it seems that the nuxt server wasn't able to communicate with keycloak, hence wasn't able to fetch the user. After changing some addresses so that keycloak was available on the same address from the browser and from within my nuxt server docker container, the nuxt server did get $auth.loggedIn=true automatically on page load if the is was logged in.
Not sure if I didn't see it, but I wished nuxt auth would give me an error if the nuxt server failed to communicate with the authorization server. Would have saved me a lot of debugging.

Using Cypress to Test an App That Relies on OAuth

I've inherited a Node.js web app that uses relies on OAuth. Whenever you visit a page the app ensures you've authenticated. Please note, there no Angular, React, Vue, etc here. Each page is straight up HTML.
I want to test this site using Cypress. My problem is, I'm stuck on the initial redirect from the auth provider. Cypress acknowledge OAuth is a challenge.
commands.js
Cypress.Commands.add('login', (credentials) => {
var settings = {
'clientId':'<id>',
'scope':'<scope-list>',
...
};
var body = `client_id=${settings.clientId}&scope=${settings.scope}...`;
var requestOptions = {
method: 'POST',
url: 'https://login.microsoftonline.com/...',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body
}
cy.request(requestOptions);
});
Then, in my test, I have:
context('Home', () => {
it('Visits Successfully', () => {
cy.login();
cy.title().should('include', 'welcome');
});
});
In the test runner, I see the login POST request is occurring. I confirmed that an access token is being received using a console.log, however, my title is empty. It's like the redirect after OAuth isn't happening in Cypress. However, when I visit the site in the browser, the redirect is happening as expected.
What am I missing?
What you might be missing is confusing between the actual UI flow and the programmatic flow of doing OAuth with a 3rd party website.
What you would want to do is to complete the programmatic login and then send the required parameters to your OAuth callback URL for your app manually in the test code.
an example is given here (though it uses a different grant type it gives you an idea) https://auth0.com/blog/end-to-end-testing-with-cypress-and-auth0/#Writing-tests-using-Cypress-Login-Command
another issue on the cypress github that deals with a similar problem
https://github.com/cypress-io/cypress/issues/2085
this also might help:
https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/logging-in__single-sign-on/cypress/integration/logging-in-single-sign-on-spec.js

VueJS (resource, router and #websanova/vue-auth) login with JWT tokens refresh token error

I'm building an app using VueJS and Electron, now I'm trying to create a login using the #websanova/vue-auth package and everything goes well (login, logout, route protection, etc..) the only thing I'm stuck on is that everytime I log in and refresh or restart electron, it kicks me back to the login page. The weird thing is, it refreshed the token successfully, if I look in the localstorage it is updated and when I try to make a manual request using a REST client the token works. I just can't get into the app using that token.
I'm using the latest versions of VueJS, vue-router, vue-resource and #websanova/vue-auth as of today (19-sep-2016).
The API side is a Laravel 5.3 installation and I'm using the tymondesigns/jwt-auth package to handle the JWT tokens.
this is how I use my routes:
'/': {
auth: true,
name: 'dashboard',
component: HomeView
},
'/login': {
auth: false,
name: 'login',
component: LoginView
}
The views are being compiled using browserify and vueify.
My login function is like this:
this.$auth.login({
body: this.body,
success: function () {
this.loading = false;
},
error: function () {
this.error.status = true;
this.loading = false;
this.body.password = '';
},
rememberMe: true
});
If you need more information in order to be able to help me, just let me know.
Edit: If you want to take a look, here are the links to the repo's:
API: https://github.com/luukhoeben/rmi-app-api
Electron app: https://github.com/luukhoeben/rmi-app-electron
Thanks,
Luuk
If you set
Vue.use(Auth, {
router: Router,
http: Vue.$http,
tokenExpired: () => {
return false
}
})
Setting tokenExpired to return false, like this, you will skip the token refresh all together, which is not that bad. Downside is that your clients will have an expired token at some point and will be forced to re-login.
Another method is to try and check when the token will expire and refresh based on that.