Auto-login in nuxtjs using only token - javascript

I have a mobile app that uses laravel passport as a mean of authentication (no problem here). In the mobile app, I added a button that will theoretically redirect me to my web app (same auth endpoint as the mobile) and automatically logs me in. How can I achieve this using nuxtjs (with nuxt auth already used)? Can't find a way to login using only the token in nuxt auth or hack it.
Please feel free to add if you have any suggestions or recommendations.
I imagine something like this but no luck.
this.$auth.loginWith('local', {
data: {
token: token <--------------------------- token from url (originated in mobile app)
}
})
.then(response => {
// successful login
})
.catch(error => {
// failed login
});
This is my sample url that I want to automatically login in nuxtjs
http://example.com/redirect?token=tokenvaluehere

I had a similar problem as you, but instead of laravel passport I used jwt-auth with local Strategy.
I achieve the auto-login functionality using the code below. Maybe it helps:
const route = ''; // Route from where to get the Token
this.$auth.reset().then(() => {
this.$axios.get(route)
.then(({data}) => {
this.$auth.setStrategy('local').then(() => {
const token = this.$auth.strategy.options.tokenType
? this.$auth.strategy.options.tokenType + ' ' + data.login_token
: data.login_token
this.$auth.setToken('local', token)
this.$auth.strategy._setToken(token)
this.$auth.fetchUser()
})
})
})

Related

How to expire a session in Laravel SPA "laravel_session" cookie?"

I currently have a application with Laravel + Sanctum + Vue SPA + Apollo GraphQL.
I'm trying to make a session expire just like in a normal Laravel application but i can't achieve this.
First I make a request to trigger the csrf-cookie of Sanctum on frontend:
await fetch(`${process.env.VUE_APP_API_HTTP}/api/csrf-cookie`, {
credentials: 'include'
})
It generates 2 cookies on browser:
XSRF-COOKIE and laravel_session
On login I use apollo and store the auth-token after make a login request:
const data = await apolloClient.mutate({
mutation: Login,
variables: credentials
})
const token = data.data.login.token
await onLogin(apolloClient, token)
export async function onLogin (apolloClient, token) {
if (typeof localStorage !== 'undefined' && token) {
localStorage.setItem(AUTH_TOKEN_NAME, token)
}
....
So i pass the token and cookie to apolloClient link prop, but i'm not sure if it is needed to pass the XSRF-TOKEN.
const authLink = setContext(async (_, { headers }) => {
const token = localStorage.getItem(AUTH_TOKEN_NAME)
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
'XSRF-TOKEN': Cookie.get('XSRF-TOKEN'),
}
}
})
Here is the problem: The login session never expires, even with the cookie laravel_session, i already tried to pass laravel_session as a header on my link connection but it doesn't seems to work.
My Laravel session.php is set 'expire_on_close' => true to be sure i can test it i close the browser and re-open, also i'm sure the cookie is set to expire on close because it says on browser cookies info.
Any idea how can i make the laravel session work on a SPA?
If you are using cookies to manage the session, your .env file should look like this:
SESSION_DRIVER=cookie
You can also define the session lifetime below
SESSION_LIFETIME=120
Suggestion: set lifetime to 1 minute, do a login and wait to see if it expires. Let me know!

Cypress Login with CSRF token

I would really like to expand on this topic "Logging in with CSRF token" as I have been banging my head against a wall for weeks now and I can't be the only one with this problem. All topics about logging in via POST or logging in with CSRF inevitably lead back to the above link.
Yet the recipes described in this link do not seem to work for me. They all assume that the CSRF token is created once you visit the Login page. But on our site, the CSRF token is only created once you login.
I tested with Postman and there is no CSRF token in the HTML or in the header before you are logged in.
I also tested it in Cypress with the following code:
describe('gimme dat csrf token', () => {
it('try to get the csrf token', () => {
cy.visit(Cypress.env('url'))
cy.getCookie('YII_CSRF_TOKEN')
.then(async (c) => {
cy.log(c.value)
return c.value
})
})
})
This will return an error as there is no YII_CSRF_TOKEN
Type Error
Cannot read properties of null (reading 'value')
If I add a login step before, it will return the value of the CSRF token as expected:
import {Login} from "../../pages/login/Login";
describe('gimme dat csrf token', () => {
it('try to get csrf token', () => {
cy.visit(Cypress.env('url'))
login.loginCredentials(Cypress.env('userEmail'), Cypress.env('userPass')) //added login
cy.getCookie('YII_CSRF_TOKEN')
.then(async (c) => {
cy.log(c.value)
return c.value
})
})
})
Therefore strategies #1 (parse token from HTML) and #2 (parse token from response headers) from the above link can not work.
Recipe #3 is also not feasible as we have several live systems to test and we can't expose a /csrf route
This only leaves us with the strategy #4, which we have been using so far.
Any ideas or are we stuck with adding the "manual" login step to every single spec file?
I think strategies #1 & #2 rely on the browser remembering credentials and supplying them to the login page, as happens with the Stackoverflow page - you don't have to log in every time you visit.
The main difference is you have used cy.visit() instead of cy.request() as shown in the recipes.
If you still are not able to successfully grab the token, try using your login with cy.session(). It will only call the login function once per session.
/*
Enable use of cy.session() and new behavior to handle caching
and restoring cookies, localStorage, and sessionStorage.
*/
Cypress.config('experimentalSessionSupport', true)
describe('...', () => {
beforeEach(() => {
cy.session(() => {
login.loginCredentials(Cypress.env('userEmail'), Cypress.env('userPass'))
})
})
it('try to get csrf token', () => {
cy.getCookie('YII_CSRF_TOKEN')
.then((c) => {
cy.log(c.value)
})
})
})

Amplify w/Cognito: How do I get the Current User's Email?

I am using AWS Amplify, with Cognito for user Auth.
Users go into a user pool, and register and sign in just with email address and password.
When a user that has signed in through Cognito navigates to a certain page, I want to be retrieve their email address. How can I do this?
I am able to retrieve some user data with this code (I am using javascript/Angular):
import Auth from '#aws-amplify/auth';
...
ngOnInit(){
Auth.currentAuthenticatedUser().then((user)=>{
console.log('user = ' + JSON.stringify(user.pool))
})
}
The email does appear on the response, but I haven't yet been able to isolate the email from the returned JSON.
I've tried going through the docs, but I haven't yet found info on stuff like the attribute options I can add to currentAuthenticatedUser(), or if there is another method that is cleaner (which I assume there is).
EDIT: It looks like the following works:
Auth.currentAuthenticatedUser().then((user) => {
console.log('user email = ' + user.attributes.email);
});
But I am still hoping to understand the documentation better. I found this solution in a random github question, not the official docs. Where would I find this solution in the AWS Amplify / Cognito documentation?
import cognito from "../path/to/your/config/cognito.json";
import Amplify, { Auth, Hub } from 'aws-amplify';
...
...
useEffect(() => {
Amplify.configure({ Auth: cognito });
Hub.listen('auth', ({ payload: { event, data } }) => {
switch (event) {
case 'signIn':
console.log('Event name -> ', event, data)
// here is your name, email e.t.c.
console.log(data.signInUserSession.idToken.payload);
break
case 'signOut':
console.log('sign out')
// this.setState({ user: null })
break
default:
console.log('Unhandled use case - ' + event)
}
})
}, [])
You can check this one from the official documentation
and enable read access
General settings -> App clients -> Show details -> Set attribute read and write permissions link
and then to make sure you are fetching the updated attributes
Auth.currentAuthenticatedUser({ bypassCache: true })
Auth.currentSession()
.then((data) => {
// this data has user details in accessToken
}).catch(err => console.log(err));
The following works for me after the user is logged in...
import { Auth, Amplify } from 'aws-amplify'
console.log(Auth.user.attributes.email)

Keeping user logged in after refresh/using refresh token with Google OAuth2 in React app

I’m building a React app where a key part of the functionality is a user can sign into their Google account and then access a feed of their most recent Google Drive/Docs mentions and notifications. A user arrives at my site where I load the Google OAuth2 client with my client_id, apiKey, scope and discoveryDocs, and they can click a button to sign in. For convenience, I’d like the user to not have to re-login and re-auth with their Google account every time they use the app or the app refreshes, I’d like the login information to be saved across sessions. For this I’ll use localStorage to start but eventually integrate a database like Firebase.
After looking through the JavaScript client Google OAuth2 docs I understand how most things work - understand the data and methods stored in the GoogleUser, GoogleAuth, etc objects. I’m having a little trouble with access and refresh tokens. I recognize that you can get the authenticated user’s information through gapi.auth2.getAuthInstance().currentUser.get() and gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse() returns an object with a lot of what I think I need like id_token, access_token and metadata like expires_at and token_type. I also see the grantOfflineAccess() method from which I extract response.code, but I’m not quite sure which of these tokenized strings is the right one to use and how I need to use it.
This FAQ from Google (https://developers.google.com/api-client-library/javascript/help/faq) is somewhat helpful but advises to Refresh the token by calling gapi.auth.authorize with the client ID, the scope and immediate:true as parameters., but gapi.auth.authorize is noted by Google in the client JS OAuth2 library as being incompatible with the more widely used and heavily documented api.auth2.init and signIn.
I also have a vague idea from posts like Google OAuth2 API Refresh Tokens that I need to follow server-side OAuth2 instructions and I can only get this refresh_token through a server-side call, but I’m still at a bit of a loss. I’ll caveat and say I’m more of a front end developer/designer so I'm shaky on my node and server-side skills.
TL;dr: I don't know how to keep my users who signed in via Google OAuth2 signed in after a refresh. I have an idea it's due to refresh_token and access_token and I have access to them but I don't know what to do after that, in terms of sending data to Google servers, getting information back, and setting the token information for the given user when they return.
Here's my method that calls on componentDidMount (basically when my app first loads):
loadGoogleClient = () => {
gapi.load("client:auth2", () => {
gapi.auth2.init({
'client_id': my-client-id,
'apiKey': my-key,
'scope': "https://www.googleapis.com/auth/drive.readonly",
'discoveryDocs': ['https://content.googleapis.com/discovery/v1/apis/drive/v3/rest']
})
// Listen for sign-in state changes.
console.log(`User is signed in: ${gapi.auth2.getAuthInstance().isSignedIn.get()}`);
gapi.client.load("https://content.googleapis.com/discovery/v1/apis/drive/v3/rest")
.then(() => { console.log("GAPI client loaded for API");
}, (error) => { console.error("Error loading GAPI client for API", error);
});
console.log('Init should have worked');
});
}
And here's my code that's onClick on my Signin button:
authGoogle = () => {
gapi.auth2.getAuthInstance()
.signIn({scope: "https://www.googleapis.com/auth/drive.readonly"})
.then(function() { console.log("Sign-in successful"); },
function(err) { console.error("Error signing in", err); });
}
If you are using the client lib (the gapi api) there is no need for a refresh token... Once logged in it should persist across sessions and refreshes... The issue is the code...
1) Include this in your index.html in the head section:
<script src="https://apis.google.com/js/api.js"></script>
2) Here is a component that will handle auth using the gapi lib and render a button conditionally (The code is self-explanatory but if you have a question just ask...)
import React from 'react';
class GoogleAuth extends React.Component {
state = { isSignedIn: null };
componentDidMount() {
window.gapi.load('client:auth2', () => {
window.gapi.client
.init({
clientId: '<your client id here...>',
scope: 'email', // and whatever else passed as a string...
})
.then(() => {
this.auth = window.gapi.auth2.getAuthInstance();
this.handleAuthChange();
this.auth.isSignedIn.listen(this.handleAuthChange);
});
});
}
handleAuthChange = () => {
this.setState({ isSignedIn: this.auth.isSignedIn.get() });
};
handleSignIn = () => {
this.auth.signIn();
};
handleSignOut = () => {
this.auth.signOut();
};
renderAuthButton() {
if (this.state.isSignedIn === null) {
return null;
} else if (this.state.isSignedIn) {
return <button onClick={this.handleSignOut}>Sign Out</button>;
} else {
return <button onClick={this.handleSignIn}>Sign in with Google</button>;
}
}
render() {
return <div>{this.renderAuthButton()}</div>;
}
}
export default GoogleAuth;
Now you can simply use this component/button anywhere in your app... Meaning if you have a Navigation component simply import it there and use it as a button login / log out...

Using Firebase Cloud Messaging + React and firebase.messaging().getToken() is different each refresh

I'm building a React web app on Gatsby and I'm trying to incorporate push notifications using FCM. I have the firebase-messaging-sw.js service worker in place and I am trying to get a token by this method in my app:
messaging
.requestPermission()
.then(() => {
console.log('Permission received.');
return messaging.getToken();
})
.then(token => {
console.log(token);
// Save token with user.
return this.saveFcmToken(token, authUser);
})
.catch(error => {
console.log('Error occurred.', error);
});
I call this snippet inside a firebase.auth.onAuthStateChanged() to get the user.
The problem is I get a different token each time. I haven't seen this type of issue documented after extended search.
Any ideas?
It turns out this was down to https://github.com/gatsbyjs/gatsby/issues/9770
In develop mode, Gatsby apparently unregisters service workers. Using a production build got rid of the problem.

Categories

Resources