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?
Related
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 added authentication for my Express API following this guide and after testing my secret-routes everything seems to work properly. Now my question is how can this be used in an Ember app login page. After receiving the secret token after a successful login how does the browser know you are signed in. How would one log out? How does the ember application know who is signed in? Is there any thing in particular security wise that I should be at tentative to while working on this?
You should use addons to handle most of the heavy lifting for you.
ember-simple-auth-token has setup directions that have you create a login route which will take a username / password and send it to your server for validation. The token response will then be available in your app until the user logs out.
The example looks like
import Controller from '#ember/controller';
import { inject } from '#ember/service';
export default Controller.extend({
session: inject('session'),
actions: {
authenticate: function() {
const credentials = this.getProperties('username', 'password');
const authenticator = 'authenticator:token'; // or 'authenticator:jwt'
this.get('session').authenticate(authenticator, credentials);
}
}
});
You also create the logout route which handles logging out of your app as well as sending any logout request to the server.
If possible you should align your server to the defaults, but you can configure nearly everything.
Authentication Options
ENV['ember-simple-auth-token'] = {
tokenDataPropertyName: 'tokenData'; // Key in session to store token data
refreshAccessTokens: true, // Enables access token refreshing
tokenExpirationInvalidateSession: true, // Enables session invalidation on token expiration
serverTokenRefreshEndpoint: '/api/token-refresh/', // Server endpoint to send refresh request
refreshTokenPropertyName: 'refresh_token', // Key in server response that contains the refresh token
tokenExpireName: 'exp', // Field containing token expiration
refreshLeeway: 0 // Amount of time to send refresh request before token expiration
};
We've been very happy with this addon in production for 3 years and I'd highly recommend it.
I have a system where the authentication is based around JWT. I have a JWT auth token, and a refresh token on the client. The refresh token is stored in the database on the server, and is used to refresh the JWT once the JWT expires every 12 hours - pretty standard setup I believe.
My issue is incorporating it in to my React Native project and handling token expiration. The user will likely be performing many API calls to their "protected" areas which require the JWT, but once the JWT expires it will send back a "UnauthorizedError" like so:
router.use('/protected', ejwt({secret: config.secret}));
//catch any errors if the JWT is not valid
router.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
res.status(401).send({
success: false,
error: "UnauthorizedError"
});
}
});
This will send back to the RN app that the JWT has expired, but at this point I would like the app to send another request back to the server generate a new JWT, but I'm not sure of a way to do this most efficiently for every API call initiated from my app. Each API call could call a function to check the server if the returned value is "UnauthorizedError", but I know this isn't the best way:
return AuthApi.someApiCall(token).then(response => {
if(response.success){
//do some success stuff
}else{
//this seems bad to be repeated for every API call
checkToken(response).then(() => {
dispatch(updateAuthToken());
});
}
})
I've been looking around and Redux middleware looks promising, but I don't know if there's a more established way perhaps, or anyone has any other thoughts?
Thanks!
These are a couple general questions to really see different implementations of detecting authentication and authorization using Aurelia. All of this is speaking within the context of a secured back-end service.
Say you are using cookie authentication with the server. How are you acknowledging that cookie in Aurelia to display to the user that they are logged in?
In the Aurelia documentation(seen here), we can see the following:
class AuthorizeStep {
run(navigationInstruction, next) {
if (navigationInstruction.getAllInstructions().some(i =>
i.config.settings.roles.indexOf('admin') !== -1)) {
var isAdmin = /* insert magic here */false;
if (!isAdmin) {
return next.cancel(new Redirect('welcome'));
}
}
return next();
}
}
What does /* insert magic here */ look like for you? What should it look like?
The app I am currently working on requests a token from the server at the 'login' route using XHR. If this request is successful, and a token was received from the backend, then the token is stored in a cookie and we route away from the login page to the main content of the app. We could then set a global variable 'loggedIn' to display that the user is logged in, etc. Each time we make further requests to the backend via XHR, we send the token with the request.
The 'magic' in the authorize step is just some logic that checks to see if the user is logged in, or in the example above, an admin.
I have a working oauth2 authentication process where I get an access token (eg from facebook) using ember simple auth, send it to the back end which calls fb.me() and then uses JWT to create a token. This token is then sent back to the ember app, which then has to send it with every server request, include those requests made by ember-data.
I also need to have this token available after a browser reload.
I have tried many options, where I set a property 'authToken' on the session - I believe that this uses local storage to persist the authenticated session.
But I always seem to have trouble with coordinating the retrieval of this token - either I don't have access to the session, or the token is no longer on the session, or I can't change the ember data headers.
Does anyone have a working simple example of how this can be done - I think it should be easy, but I'm obviously missing something!
Thanks.
Update
The only thing I've been able to get working is to use torii as shown below, but the session content is still lost on refresh - I can see its still authenticated, but its lost the token I set here. So I'm still looking for a real solution.
authenticateWithGooglePlus: function () {
var self = this;
this.get('session').authenticate('simple-auth-authenticator:torii', 'google-oauth2')
.then(function () {
resolveCodeToToken(self.get('session'), self);
});
}
resolveCodeToToken gets the bearer token from the server, sets it on the session and then transitions to the protected page:
function resolveCodeToToken(session, route) {
var authCode = session.content.authorizationCode;
var type = session.content.provider.split('-')[0];
$.ajax({
url: 'http://localhost:4200/api/1/user/auth/' + type,
data: {authCode: authCode}
}).done(function (response) {
// todo handle invalid cases - where user is denied access eg user is disabled
session.set('authToken', response.token);
route.transitionTo('activity', moment().format('DDMMYYYY'));
});
}
And I have a custom authorizer for putting the token (stored in the session) on every request:
import Base from 'simple-auth/authorizers/base';
export default Base.extend({
authorize: function(jqXHR, requestOptions) {
var accessToken = this.get('session.content.authToken');
if (this.get('session.isAuthenticated') && !Ember.isEmpty(accessToken)) {
jqXHR.setRequestHeader('Authorization', accessToken);
}
}
});
I'm not sure why this.get('session.content.authToken') would be undefined after a refresh, I thought by default the session was persisted in local storage. The fact that it is authenticated is persisted, but thats useless without the token since the server will reject calls to protected endpoints.
You'd want to implement your own custom authenticator that first gets a token from Facebook and then sends that to your own server to exchange it for a token for your app. Once you have that you get authorization of ember-data requests as well as session persistence etc. for free.
Have a look at this example: https://github.com/simplabs/ember-simple-auth/blob/master/examples/7-multiple-external-providers.html