I have a ServiceStack 4 API project and an MVC 5 web project and I'd like them to share the user's session data.
Both projects have been configured to use the same Redis instance as a cache.
My MVC controllers inherit from ServiceStackController.
The session is being created by the API (I can see the return cookies and query Redis for the object by session id).
Where I'm struggling is how the MVC project becomes aware of the session id. Initially I thought ShareCookiesWithBrowser on the JsonServiceClient might do the trick, but it doesn't seem to.
So, I've added Request and Response filters to the JsonServiceClient to add the website's cookies to API requests and also add API response cookies to the websites response cookies. This seems to work in the sense that the API call and web request ss-id and ss-pid cookies match, and the session object is available in Redis. Unfortunately, when I call the UserSession property on the MVC controller (from the ServiceStackController base), a NullReferenceException is thrown.
If anyone could let me know what I'm missing or doing wrong, that'd be much appreciated.
Update
Just figured out that the Cache property on the ServiceStackController was null. I guess it is normally property injected by Funq, but we're using SimpleInjector and avoiding property injection.
It would still be nice to know if I have to manually pass the cookies back and forth via the JsonServiceClient. It just seems a bit hacky in something as generally well thought out as ServiceStack - I still can't help but think I'm missing something.
Thanks,
Steve.
You don't need to use the HTTP Service Clients to call ServiceStack services from MVC as you can just call the ServiceStack Services directly with C#. Here are some examples from the new ServiceStack + MVC test project showing how to login:
public ActionResult Login(string userName, string password, string redirect=null)
{
if (ModelState.IsValid)
{
try
{
using (var authService = ResolveService<AuthenticateService>())
{
var response = authService.Authenticate(new Authenticate
{
provider = CredentialsAuthProvider.Name,
UserName = userName,
Password = password,
RememberMe = true,
});
// add ASP.NET auth cookie
FormsAuthentication.SetAuthCookie(userName, true);
return Redirect(string.IsNullOrEmpty(redirect) ? "/" : redirect);
}
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
}
}
var session = SessionAs<CustomUserSession>();
return View("Index", session);
}
And logging out:
public ActionResult Logout()
{
using (var authService = ResolveService<AuthenticateService>())
{
var response = authService.Authenticate(new Authenticate {
provider = AuthenticateService.LogoutAction,
});
// add ASP.NET auth cookie
FormsAuthentication.SignOut();
}
return Redirect("/");
}
You can play with the MVC test project which is deployed at http://mvc.servicestack.net/ - it shows logging in, logging out and accessing ServiceStack's typed Session from MVC.
Also checkout the Session Services in the project which shows how to retrieve, edit and adding custom logic in the Custom UserSession's OnAuthenticated() event to copy over non-authenticated session data over into the authenticated Type Session as described in this earlier answer.
This project does make use of some of the newer API's added in v4.0.32+ that's now available on MyGet.
Related
I am new to Next.js and I am struggling with the authentication system using jwt token. I want to know what is the best / standard way to store the jwt token and routing with the authentication system. I have been trying different approaches, from different tutorials/articles, but do not quite understand it. Here are what I have tried.
When the user login, it sends username/password to a separated API server (ex. new project that handles backend stuff), the server will respond with the access-token, then in Next.js project, I set the cookie with that received token. In Next.js project, protected routes will be wrapped with a withAuth hoc, which will check for the token in a cookie. The problem with this approach is that it is vulnerable to XSS because the cookie has no httpOnly flag.
This is similar to 1.) but using localStorage, the problem is access-token could not be sent to the server on the first request. (This one I'm not sure, but in my understanding, in every HTTP request, I must stick my access-token manually, so what about requests that I have no control over? ex. first request or using <a> tag).
I wrote authentication backend inside Next.js server (custom express server). When the user login, the server will validate it and then set an httpOnly cookie. Then the problem is, with client-side routing (go to URL using Next.js Router), it could not check for token. For example, if a page is wrapped with withAuth hoc, but it cannot access the token inside cookies with javascript.
And I've seen a lot of people, in getInitialProps of the protected route, they only check for existence token in cookie / localStorage, then what if the token is being revoked or blacklisted, how do they handle it because they did not send the token to the server? Or do I have to send the token to the server in every client-side page change?
Since we are on quarantine I have enough time to answer this question. It will be a long answer.
Next.js uses the App component to initialize the pages. _app page is responsible for rendering our pages. We authenticate users on _app.js because anything that we return from getInitialProps can be accessed by all of the other pages. We authenticate user here, authentication decision will be passed to pages, from pages to header, so each page can decide if the user is authenticated or not. (Note that it could be done with redux without prop drilling but it would make the answer more complex)
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {};
const user = process.browser
? await auth0.clientAuth()
: await auth0.serverAuth(ctx.req); // I explain down below
//this will be sent to all the components
const auth = { user, isAuthenticated: !!user };
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps, auth };
}
render() {
const { Component, pageProps, auth } = this.props;
return <Component {...pageProps} auth={auth} />;
}
}
If we are on the browser and need to check if a user is authenticated, we just retrieve the cookie from the browser, which is easy. But we always have to verify the token. It is the same process used by browser and server. I will explain down below. But if we are on the server. we have no access to the cookies in the browser. But we can read from the "req" object because cookies are attached to the req.header.cookie. this is how we access to cookies on the server.
async serverAuth(req) {
// console.log(req.headers.cookie) to check
if (req.headers.cookie) {
const token = getCookieFromReq(req, "jwt");
const verifiedToken = await this.verifyToken(token);
return verifiedToken;
}
return undefined;
}
here is getCookieFromReq(). remember we have to think functional.
const getCookieFromReq = (req, cookieKey) => {
const cookie = req.headers.cookie
.split(";")
.find((c) => c.trim().startsWith(`${cookieKey}=`));
if (!cookie) return undefined;
return cookie.split("=")[1];
};
Once we get the cookie, we have to decode it, extract the expiration time to see if it is valid or not. this part is easy. Another thing we have to check is if the signature of the jwt is valid. Symmetric or asymmetric algorithms are used to sign the jwt. You have to use private keys to validate the signature of symmetric algorithms. RS256 is the default asymmetric algorithms for APIs. Servers that use RS256, provide you with a link to get jwt to use the keys to validate the signature. You can either use [jwks-rsa][1] or you can do on your own. You have to create a certificate and then verify if the token is valid.
Assume that our user authenticated now. You said, "And I've seen a lot of people, in getInitialProps of the protected route, they only check for existence token in cookie / localStorage,". We use protected routes to give access only to the authorized users. In order to access those routes, users have to show their jwt tokens and express.js uses middlewares to check if the user's token is valid. Since you have seen a lot of examples, I will skip this part.
"then what if the token is being revoked or blacklisted, how do they handle it because they did not send the token to the server? Or do I have to send the token to a server in every client-side page changing?"
with verifying token process we are 100% sure if the token is valid or not. When a client asks the server to access some secret data, the client has to send the token to the server. Imagine when you mount the component, component asks the server to get some data from the protected routes. The server will extract the req object, take the jwt and use it to fetch data from the protected routes. Implementation of the fetching data for browser and server are different. And if the browser makes a request, it just needs the relative path but the server needs an absolute path. As you should know fetching data is done getInitialProps() of the component and this function executed on both client and server. here is how you should implement it. I just attached the getInitialProps() part.
MyComponent.getInitialProps = async (ctx) => {
const another = await getSecretData(ctx.req);
//reuslt of fetching data is passed to component as props
return { superValue: another };
};
const getCookieFromReq = (req, cookieKey) => {
const cookie = req.headers.cookie
.split(";")
.find((c) => c.trim().startsWith(`${cookieKey}=`));
if (!cookie) return undefined;
return cookie.split("=")[1];
};
const setAuthHeader = (req) => {
const token = req ? getCookieFromReq(req, "jwt") : Cookies.getJSON("jwt");
if (token) {
return {
headers: { authorization: `Bearer ${token}` },
};
}
return undefined;
};
export const getSecretData = async (req) => {
const url = req ? "http://localhost:3000/api/v1/secret" : "/api/v1/secret";
return await axios.get(url, setAuthHeader(req)).then((res) => res.data);
};
[1]: https://www.npmjs.com/package/jwks-rsa
With the introduction of Next.JS v8, there are examples placed in the NextJS example page. The basic idea to follow is:
JWT
Using cookies to store the token (you may choose to further encrypt it or not)
Sending the cookies as authorization headers
OAuth
Using a third-party authentication service such as OAuth2.0
Using Passport
This question might need an updated answer, now middlewares are there in Next.js 12 (october 2021): https://nextjs.org/docs/middleware
I am drafting a comprehensive answer to explain auth in Next.js more deeply, you can follow the progress there on GitHub
Here I'll try to propose a summary for Next.js, using middlewares.
Verifying the token after auth and redirecting accordingly
Most of the answer from #Yilmaz from april 2020 is still relevant. However, previously, we had to use getInitialProps in _app to process the request OR a custom server.
This is no longer the case.. Using a middleware let's you achieve a similar purpose, with cleaner code. Because middleware are specifically designed for such use cases.
Here, I suppose you get a JWT access token using an asymetrical algorithm like RS256, exactly like in this previous answer.
Here is a possible implementation:
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
const removeCookie = (res: NextResponse, cookieName: string) => {
res.headers.append("Set-Cookie", `${cookieName}=; Max-Age=-1; Path=/`);
return res;
};
export default async function middleware(
req: NextRequest,
ev: NextFetchEvent
) {
const { pathname } = req.nextUrl;
const isPublic = isPublicRoute(pathname);
if (isPublic) {
return NextResponse.next();
}
const accessToken = req.cookies[TOKEN_PATH];
if (!accessToken) {
return NextResponse.redirect(LOGIN_HREF);
}
const isValidToken = await checkAccessToken(accessToken);
if (!isValidToken) {
let res = NextResponse.redirect(LOGIN_HREF);
res = removeCookie(res, TOKEN_PATH);
return res;
}
return NextResponse.next();
}
How to verify the token
In my example, the checkAccessToken should verify the token (not decode, verify the signature).
This is where things are the most complicated imo.
When using the RSA256 algorithm
You also get a PUBLIC certificate (in addition to the SECRET key that must be... kept secret). Eventhough you do the check in the middleware, which is private and server-only code, that's good news because it means you could even use it in the browser, in theory.
So, you can either fetch the token validation endpoint provided by your auth server, or verify the token yourself.
Fetching is not the recommended option because it might break Vercel/Next edge capabilities and add latency, according to the documentation.
I must admit that I did not succeed to verify the token yet using Next.js :) I'll update this answer if I manage to have a code sample that works.
When using a symmetrical encryption
You have only a PRIVATE secret passphrase. It means that the decoding have to happen server-side (good news, you are writing a middleware).
Login/logout
This doesn't change with middlewares. You store your access token as an httpOnly cookie. When logging out, you unset this cookie.
Managing those Set-Cookies headers are the responsibility of your auth server.
This is a basic workflow but it should work. You can then add a refresh token in the mix with a similar approach.
About token revokation
If you verify the token in your middleware, there is no immediate revokation mechanism for the access token. Because there is no call to a database.
Therefore, in this scenario, you'd want to opt-in for short lived access token (eg 5 minutes) coupled with a refresh token. You can revoke the refresh token, so basically revoking works but takes a few minutes.
If a 3rd party server verifies the token: then it could check for blacklisted tokens.
Caveats
Also, some piece of advice: most articles, tutorials etc. online are focused on server-to-server communication. Or client-to-API. They completely suck when it comes to check authentication before accessing web pages.
For instance, setting the Authorization header is not possible in the browser. It works only when communicating with an API. Cookies are mandatory for web pages.
Even then, if this API is meant to be called from a browser, it should preferably accept a cookie.
When discussing with experts on the field, you need to always clarify the Next.js use case.
Open questions: about session-based authentication
Some frameworks seem to prefer relying on the database. They store a hashed token in the db, which acts as a session. If you want to check auth, you need a server that will check the user's token against the stored token (= checking that there is an active session with this token).
I am thinking of Meteor for instance.
I couldn't find the name of this mechanism and its actual relation to JWT however. Are they simply variations of the JWT approach?
Next.js official authentication doc is not showing middlewares at the time of writing, but instead use getServerSideProps. I really don't like this pattern.
It uses a kind of session system but I am not clear about the internals of it, I am not even sure of the name (is that session-based auth?).
Vercel edge handles examples shows how to secure an API route, but not a page (at the time of writing)
I am new to Angular and implementing authentication for users.
Most of suggestions on the web propose to save session/username in local storage and when user is back to the app, check local storage in order to display proper navigation (in my navbar I have different nav buttons for private and public views).
However, I found this solution having some drawbacks. For example, if session on server expired, or local storage was manually added, then when app will be initialising, it will show wrong buttons in navbar.
After that I came to solution - to use service before showing navbar buttons, to send request to server to verify if user from local storage with his session is currently active. And only after that I will display nav buttons.
Here comes a question: is it the most efficient way to proof check if user from local storage is logged in and session is active?
There is another way, which I was thinking, but didn't find solution.
Since my angular webapp and nodejs server are located in two different places, is it possible for the webapp to check authentication status (make a request from webapp server to my nodejs server) when index.html is requested and respond with pre-rendered navbar and user status (logged in or not)?
Thanks.
P.S. I am using PassportJS and Express on server side.
The best practise is to use AuthGuard and implement CanActivate to check whether user can view a particular part of the application. Also an authentication service is used normally to let the user login to system and gain an access token.
This access token is then used as Authorisation-Header on each request to server (this is where they will be in sync).
You will need to check for JWT/or any other type token on load which contains user information plus session time out.
If the token is invalid you simply redirect the user to login, otherwise it will allow user to go where they wanted to.
A practical example can be found here.
To have navbar showing different elements for authenticated and non-authenticated users, one of possible solutions will be
To use some "/auth-check" request in authentication.service.ts, which will have trigger every time an event of the result of checking authorisation of current user
...
interface ShareObj { [id: string]: any; }
...
currentUserId: ShareObj = {};
currentUserUsername: ShareObj = {};
public authenticatedBehavior = new ReplaySubject(1);
authCheck(): any {
return this.http.get('/api/auth-check')
.map((resp: any) => {
if (resp.authanticated) {
this.currentUserId['global'] = resp.user.id;
this.currentUserUsername['global'] = resp.user.username;
this.authenticatedBehavior.next(true);
} else {
this.authenticatedBehavior.next(false);
this.currentUserId['global'] = null;
this.currentUserUsername['global'] = null;
}
return resp;
})
.catch(e => {
this.authenticatedBehavior.next(false);
this.currentUserId['global'] = null;
this.currentUserUsername['global'] = null;
});
}
So, in navbar.component.ts there should be a listener for this event:
ngOnInit() {
this.authService.authenticatedBehavior
.subscribe(
data => {
// do change of UI of navbar depending if user logged in or not
}
);
}
To have error-iterceptor.ts file, where you should "catch" all failed requests and check them for Unauthorised response (401). If you catch such response, do authCheck() in authentication.service.ts to make sure that session of current user expired and notify all components which listen for authenticatedBehavior
I'm building a SPA that will be sitting in a WebAPI/OWIN app (that will be hosted in IIS) that currently has no MVC components what so ever and the / route will just be index.html.
The whole site will need you to log into Azure AD before you can do anything and we'll then pass a bearer token to the WebAPI calls that are made.
How do you make every request to a static file (or at least, every HTML file) require you to be logged in?
I will tell you how I did it, and how it works for me.
I am using windows authentication, and here is how I configured it:
OwinHttpListener listener = appBuilder.Properties[typeof(OwinHttpListener).FullName] as OwinHttpListener;
listener.Listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
Then, according to this stackoverflow answer, put the following code between your auth middleware (or the auth code like the above code) and the components you want to protect. It will check to ensure that each request is authenticated.
app.Use(async (context, next) =>
{
var user = context.Authentication.User;
if (user == null || user.Identity == null || !user.Identity.IsAuthenticated)
{
context.Authentication.Challenge();
return;
}
await next();
});
I haven't tested this, but it is what I'd try first so I'm hoping it puts you on the right track.
Configure your app so OWIN is serving all static files via the StaticFilesMiddleware. This article talks about how to do that
Before you register the static file middleware (with the .UseStaticFiles extension method) create and register your own RequireAuthenticationMiddleware that checks if the request is authenticated and if it isn't returns the appropriate response (401, 403 or whatever). You'll want to register this RequireAuthenticationMiddleware after you configure the OWIN Auth middleware so that the auth details are in the OWIN context.
I haven't attempted that using OWIN middleware yet, but you could always fall back to using a HTTP module that checks for the presence of your auth cookie or bearer token?
I'm working in a web app which handle resources from a Mongo database, for such resources I'd like to offer an API, so a future mobile application can seize it or consume it from a raw client.
However I'd like to have web app consuming same API, here is where I get a bit confused about how to properly implement this.
Here is what I've done so far:
API Auth:
app.route('/api/auth/')
.post(function (request,response) {
var email = request.body.email;
var password = request.body.password;
var login = new Account({"local.email":email,"local.password":password});
Account.findOne({"local.email":email}, function (err,user) {
if (err) {
response.send(500);
}
if (!user) {
response.send(404);
}
else {
user.validPassword(password, function (err,matched) {
if (err) {
response.send(500);
}
if (matched) {
var uuidToken = uuid.v4();
redisClient.set(uuidToken,user._id,redis.print);
redisClient.expire(user._id,100);
response.send(uuid);
}
else {
response.send(403);
}
});
}
});
});
So basically I receive consumers username and password, I authenticate it against database, If it matches I reply a token, (actually an UUID). That token gets stored at Redis paired with the user id in databse. Every future request to any API route will verify for such token existance.
Here I wonder:
How should I manage the token TTL, and renewal upon future requests?
How can I control requests per time windows limits?
Is there any security caveat in the approach I'm taking?
Website Auth:
Basically I perform SAME username-password authentication against database and I then:
1. Start a new server session.
2. Naturally, offer back a cookie with session ID.
3. I create then the Redis UUID and user ID record, which API will check. I guess this is OK as there's any sense in requesting POST /api/auth authenticating again.
Here I wonder:
Is this a best approach?
Should I include any token salt to distinguish a pure API consuming request from a request from web app?
Is there any security caveat in the approach I'm taking?
Should I include more tokens?
This is example of POST /login:
app.route('/login')
.post(function (request,response,next) {
var email = request.body.email;
var password = request.body.password;
var login = new Account({"local.email":email,"local.password":password});
Account.findOne({"local.email":email}, function (err,user) {
if (err) {
response.redirect('/error');
}
if (!user) {
var cookie = request.cookies.userAttempts;
if (cookie === undefined) {
response.cookie('userAttempts',1);
}
else {
response.cookie('userAttempts',(++cookie));
}
response.redirect('/');
}
else {
user.validPassword(password, function (err,matched) {
if (err) {
// Redirect error site or show err message.
response.redirect('/error');
}
if (matched) {
var session = request.session;
session.userid = user._id;
var uuidToken = uuid.v4();
redisClient.set(uuidToken,user._id,redis.print);
redisClient.expire(uuidToken,900);
response.cookie('email',email);
response.redirect('/start');
}
else {
var cookie = request.cookies.passwordAttemps;
if (cookie === undefined)
response.cookie('passwordAttemps',1);
else {
var attemps = ++request.cookies.attemps
response.cookie('passwordAttemps', attemps)
}
response.redirect('/');
}
});
}
});
})
I think I could get rid of using and writing a typical session implementation and depend somehow on the similar token based auth the API has.
What you have there is on the right track and basically replaces some of the functionality of cookies. There are a few things to consider though, and you've touched on some of them already.
While using a UUID (v4 I'm guessing?) is good in that it's nondeterministic and "random", on its own the token is worthless. Should redis lose data the token no longer has any context. Nor can you enforce expirations without help from redis. Compare this to a JWT which can carry context on its own, can be decrypted by anybody with the correct key, can handle expirations, and can enforce further common application level constraints (issuer, audience, etc).
Rate limiting. There are a number of ways to handle this and few of them are tied directly to your choice of token scheme aside from the fact that you'd probably use the token as the key to identify a user across requests in the rate limiter.
Transparently passing the token in both a web app and on other clients (mobile app, desktop app, etc) can be a huge pain. In order to access private resources the user will need to pass the token in the request somewhere, likely the headers, and in the case of a web app this means manual intervention on your part to include the token in each request. This means hand coded ajax requests for all authenticated requests. While this can be annoying, at least it's possible to do, and if you're writing a single page app it's likely you'd do that anyways. The same can be said for any mobile or desktop client. Since you already have to make the HTTP request directly in code anyways, why does it matter? Now imagine the scenario where an HTTP GET endpoint, which returns an html page, can only be accessed with proper authentication. In the case of a web app the user is very likely going to access this via a browser redirect or by typing it directly into the URL bar. How is the token added to the request? Other than using cookies, which you're explicitly not using because mobile and desktop clients do not implement them, this is not really possible. However, if your API clients can always modify the HTTP request structure this isn't really a problem.
Now for a shameless plug, our team has a library we use for this. It's mostly used internally and as such is pretty opinionated on its dependencies (express, redis), but hopefully it can help you here. In fact, that library is pretty much just a JWT wrapper around what you have in place. If you decide to use it and notice any issues or deficiencies feel free to file any issues on github. Otherwise there are a whole bunch of other JWT based session management modules on npm that look promising. I would check those out regardless as there are very likely better modules out there than ours. Again, ours is used internally and came about from a pretty specific set of use cases so the chances that it captures all of yours are pretty slim. On the other hand, it sounds like you're using a similar stack so maybe the shoe fits.
If you do use ours it may seem odd that there's a split in the API surface on that module in that you can choose to store data directly in the JWT claims or in redis. This was deliberate and I think your examples illustrate a good use case for both sides. Typically what we do is store the user's email and name in the JWT claims, then store more dynamic session data in redis on their session. For example, upon logging in you'd add the issuer, audience, and user's email to the JWT claims but leave off anything related to "userAttempts". Then upon failed attempts you would add or modify the "userAttempts" on the session data stored in redis related to that JWT. Once a JWT is set it's not possible to modify its contents without generating a new one, so be aware that if you decide to keep relatively dynamic data in the JWT you'll have a constant exchange of old and new JWT's between the server and client.
I have a startup module in angularjs. This module is just to login and have public information (login, prices, newsletter...). I have many roles and for each role, i have an app (angular module). I made this architecture because i have complex module for each role and it was impossible to put all roles in one module.
So, for login, i use jsonwebtoken in node like this :
var token = jwt.sign(user, config.secureToken, { expiresInMinutes: 20*5});
res.json({ token: token, user: user });
It works perfectly. I can login into my app. After that, i have to propose a list of roles to redirect to the right module.
In angular, I have AuthHttp service that adds security headers (with token) to call rest service with $http.
How can i redirect to 'mydomain:port/anotherModule' with $location or $http ?
With this code in nodejs :
app.get('/secondModule', expressJwt({secret: config.secureToken}), function (req, res) {
res.render('restricted/secondModule/index.html');
});
NodeJs sends an html code in response and does'nt redirect...
And if i do this in my angular controller :
location.href = route;
i have this result on nodejs console :
Error: No Authorization header was found
I am not sure about the libraries you are using, but issue seems that you are loosing the token because you navigate to a altogether new page.
Based on your auth library you need to pass the token that you get after auth from one page to another.
The options here are to either use browser sessionStorage or querystring to pass the token along and at it back to the http header collection on the new page (module)
This is an old post but I recently took a long time to figure this out. I may be wrong but I believe nodeJS/expressJS can't read the token from the session storage. I believe you will need to pass the token via the request header using AngularJS.
This depends on the front end that you are using. For me, I am using AngularJS and I have to do something like this.
angular.module('AngularApp').factory('authFactory',
function($window){ //the window object will be able to access the token
var auth = {};
auth.saveToken = function(token){
$window.localStorage['token_name'] = token; //saving the token
}
auth.getToken = function(){
return $window.localStorage['token_name']; //retrieving the token
}
return auth;
}
.service('authInterceptor, function(authFactory){
return { headers: {Authorization: 'Bearer "+ authFactory.getToken()}
} //the last line gets the retrieved token and put it in req.header
Then, you just need to include 'authInterceptor' in all the http methods when you communicate with the backend. This way, nodeJS will be able to pick up the token.
You can see the Authorization field in req.header if you use the chrome developer tool and look at the Network tab. Hope this helps.