I have a Ruby on Rails backend API and frontend application written in Nuxt.js integrated with Auth0 for authentication and Axios for API communication.
The authentication works fine, however the frontend does not pass user information to backend. Only user_id and Authorization Bearer token, but not email, name and other attributes. These attributes are however available after authentication in frontend and saved/available in the Vuex store.
How to tell frontend (Axios) to include with every API request to backend, additional user attributes? Is there any syntax to access Vuex store and define global Axios attribute in nuxt.config.js or is there a simpler way to do it?
Nuxt.config.js
auth: {
redirect: {
login: '/auth/login',
callback: '/auth/callback'
},
strategies: {
auth0: {
domain: config.AUTH.DOMAIN,
client_id: config.AUTH.CLIENT_ID,
audience: config.AUTH.AUDIENCE
}
}
}
example request to backend and saving results to Vuex store
this.$axios.get('/organizations/4/projects')
.then(response => {
this.setProjects(response.data.projects);
})
.catch(error => {
console.log(error);
});
You could add the extra information (in a custom claim) to the access token, then it would be available to the backend when you make the request.
Another strategy would be to make use of the /userinfo endpoint to request the user profile, although this will require an extra call from the backend.
The tradeoffs here are the increased size of the token with custom claims vs the extra API call with the /userinfo endpoint.
Related
I am currently building a web app based on a turborepo (monorepo) in which I want to use Discord OAuth login with next-auth. Therefore I have two modules web and api, where api is my express backend with discord.js. The web app is basically a dashboard for a Discord bot.
I figured that next-auth only provides client side authentication. So my question is how can I validate the OAuth session from the client side in the best manner?
My middleware for express currently looks like this:
function throwUnauthorized(res: Response) {
res.status(401).json({ code: 401, message: 'Unauthorized' });
}
export async function isAuthorized(req: Request, res: Response, next: NextFunction) {
try {
const authorization = req.headers.authorization;
if (!authorization) {
return throwUnauthorized(res);
}
// validate token with Discord API
const { data } = await axios.get('https://discord.com/api/oauth2/#me', {
headers: { Authorization: authorization },
});
// protect against token reuse
if (!data || data.application.id !== process.env.TC_DISCORD_CLIENT_ID) {
return throwUnauthorized(res);
}
// map to database user
let user = await User.findOne({ id: data.user.id });
user ??= await User.create({ id: data.user.id });
data.user.permissions = user.permissions;
req.user = data.user;
next();
} catch (error) {
return throwUnauthorized(res);
}
}
In this approach the Discord OAuth Token would be send via the Authorization header and checked before each request that requires Authorization. Which leads to my problem: The token needs to be validated again causing multiple request to Discord API.
Is there a better way to handle this? Because I need to map Discord user profiles to database profiles. I read that you could try decode the jwt session token from next-auth, but this did not work when I tested it.
Maybe there is a whole different project structure suggested for my project. But I thought I should separate the api and web-app since I would have needed a custom express server because it includes the Discord bot and Prometheus logging functions. I am open for suggestions and your thoughts!
I need a sanity check on what I'm trying to do here.
I want to build a webapp with nextjs where people can log in with discord and as a backend API I want to use a aspnetcore web api.
I got next-auth to work with discord pretty quickly in the frontend. However I'm struggling on how to identify my frontend to my backend.
My plan at the moment is to have my backend create another JWT token and save that somewhere and then use it as the Authorization header in calls to the backend api.
next-auth has callbacks where I can edit the session and the token. So what I plan to do at the moment is just call the backendapi/createJwtToken endpoint, save it to the already existing next-auth token and then into the next-auth session.
Then I could access it anywhere and don't have to refresh until the session is gone.
I can do that with next-auth callbacks
callbacks: {
async session({ session, token, user }) {
session.backendApiToken = token.backendApiToken;
return session;
},
async jwt({ token, account }) {
if (account) { // this fires only on sign in
token.backendApiToken = "ABC - get it from backend/createToken";
}
return token;
},
Is this okay? I know how to create and validate tokens in an aspnetcore api.
Is something unsecure or strange about saving an encoded apiToken in the next-auth token? Or is this absolutely normal?
I'm trying to build SSR application using NextJS and apollo-client on the frontend, and graphql with express using (graphQL Yoga) on the backend.
I came from client side rendering background and things there are simpler than SSR when it comes to authentication, in regular client side rendering my approach to authenticate user was like:
1- once the user login after server validation, sign a JWT with current user data, then send it to the client side, and save it in localstorage or cookies, etc...
2- implement a loadUser() function and call it in the (root) App component's useEffect hook to load the user in every component (page) if the JWT in localstorage is valid.
3- if the JWT isn't there or is invalid just return user as null and redirect to login page.
so in Next.js i know we can't access localstorage cause it works server side, so we just save the token in a cookie, and the approach i implemented is painful and i was wondering if there is an pimplier way, my approach is like:
1- once the user login he calls the login mutation which sets a cookie in the req header, and return a user and any data i want.
2- in each page that requires authentication i need to get the token from the cookie to send it back in the header and i did that in getInitialProps() or getServerSideProps() cause both runs server side and have access to the request cookies in the header like so:
export const getServerSideProps = async ctx => {
const apolloClient = initializeApollo();
// get the cookies from the headers in the request object
const token = ctx.req.headers.cookie ? ctx.req.headers.cookie : null;
return {
props: {
initialApolloState: apolloClient.cache.extract(),
token: token
}
};
};
now i have access to the token in the page props and can send the token back with the req header with my apollo client like so:
let getUserQuery = await apolloClient.query({
query: GET_USER_QUERY,
variables: { id: ctx.params.id },
context: { headers: { token: token } }
});
now i have access to the token in the server side request like req.headers.token
what i wanna achieve:
1- is there an easier way to implement loadUser() that loads the user with every page render that i can implement in next.js custom _app , i found this answer but it doesn't return auth object or user in all components as he mentioned in his answer.
2- i read that if i set cookies httpOnly and credentials: "include" i have access to cookie in every request, but it seems that it doesn't work with apollo client, that would be awesome if there is an alternative approach.
3- there is apollo-link-context provided by apollo team where i can send a token or any value in every request's header using setContext() like so:
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
but since i don't have access to localstorage i can't implement it cause next runs server side, so if anyone has an implementation for this please consider sharing.
PS. i made this thread after searching and reading for like 1 week and it's my last resort to ask you guys, and thanks in advance.
get token by store
store.getState()..path.to.your.token
the problem is that the token doesn't completely update when the blind changes and I'm looking for a solution.
I am building an application using hapi.js . The clients of this application are going to be either a web application, so authentication is via JWT in the coookie or via OAuth2 clients which are going to be sending the Bearer key header.
Is there some way that the framework allows using both schemes for the same route? I want the authentication to fail if both schemes fail, but pass if either of the go through.
Look at http://hapijs.com/api#route-options under auth.strategies. This will allow you to set multiple strategies for your route. You can define the behaviour with auth.mode.
hapi supports multiple authentication strategies for a route. Register the indiviual plugins for authentication and set the default auth scheme afterwards.
var Hapi = require('hapi')
var BasicAuth = require('hapi-auth-basic')
var CookieAuth = require('hapi-auth-cookie')
// create new server instance
var server = new Hapi.Server()
// register plugins to server instance
server.register([ BasicAuth, CookieAuth ], function (err) {
if (err) {…}
server.auth.strategy('simple', 'basic', { validateFunc: basicValidationFn })
server.auth.strategy('session', 'cookie', { password: '…' })
server.auth.default('simple')
})
Each authentication scheme may require dedicated configuration (like a cookie password, a validation function, etc.) that you need to provide.
I am trying to design the authentication flow of an Ember application with a Rails backend. I basically want to authenticate users via Google/Facebook/etc., I do not want to provide an 'independent' authentication service. I do want to maintain a list of users of course on the server side, potentially merging different authentications from different sources into the same user. I will not interact on behalf of the user on Google/Facebook from the client side, but I will do that on the server side.
For the above reason I was planning to do the following:
I will use torii to fetch an auth_token on the client side and I will pass that onto the server side, where I will validate it, convert it into an access token.
I will generate a custom token on the server side which I will send back to the client and require all further API calls to be accompanied by that token. I will not share the access token with the client at all.
Would you say that this is an optimal flow?
In terms of implementation, I have been able to get auth_tokens from the different providers using the example here. I am completely unsure however:
if I need ember-simple-auth or only torii (how do these two complement each other?)
how do I pass the auth token to the server side? With the code below I can get the auth token, but is this the proper place to implement the call to the API?
export default Ember.Route.extend({
actions: {
googleLogin: function() {
var _this = this;
this.get('session').authenticate('simple-auth-authenticator:torii', 'google-oauth2').then(
function() {console.log(_this.get('session.secure.authorizationCode'));}
);
return;
},
facebookLogin: function() {
this.get('session').authenticate('simple-auth-authenticator:torii', 'facebook-oauth2');
return;
}
}
});
how do I make all further requests to the API to be accompanied by a specific token?
should I use devise on the server side to make it easier or not?
I have been implemented exactly the same kind of workflow.
I used ember-simple-auth with ember-simple-auth-torii and implemented a custom authenticator to achieve this goal.
Ember-simple-auth provides an example of a custom authenticator here .
Your custom authenticator implementation will look like the following
First get auth_token using torii
Then valid this auth_token against your backend in order to get your custom token
Your authenticate callback in your custom authenticator will basically look like the following :
authenticate: function(provider, options) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
self.torii.open(provider, options || {}).then(function(data) {
var endpoint = '/token'; // Your API endpoint used to get your cutom token
var dataToSend = { // Data sent to your endpoint
grant_type: 'authorization_code',
code: data.accessToken,
access_token: data.accessToken
};
$.post(endpoint, dataToSend).done(function(response) {
response.provider = provider;
resolve(data);
}).fail(function(response) {
response.provider = provider;
reject(data);
})
}, reject)
})
}
Once you have the custom authenticator initilized you can use it this way on your controllers :
this.get('session').authenticate(
'authenticator:customauthenticator', // Or wathever name you gave
'facebook-connect' // Any compatible torii provider
).then(function(user) {
console.log(user); // Will display ajax response from your endpoint
})
Finally, if you want your custom token to be automatically sent with all ajax request, you can use the ember-simple-auth oauth2-bearer authorizer.