How to stub (to bypass) a Sailsjs auth policy to allow for controller testing? - testing

I have a Sailsjs controller that does some stuff, but currently it is under a policy that requires authentication (i.e. isAuthenticated).
Before having applied that policy, the integration test for my controller worked fine. Now it will not work because it requires the user to login.
For reasons not stated, I cannot make a request to login before each test, so I have to find a way to skip that policy. I would like to stub it so it always lets the route go through. How would you do that in Sailsjs v0.12.11?

You can add an API key in your isAuthenticated method and check for this key. If the key is available, call the next() function.
Code snippet:
module.exports = function (req, res, next) {
if (req.isAuthenticated() || req.headers['api-key'] == 'apiKeyValue') {
return next();
} else {
if (!req.headers['api-key'] && !req.isAuthenticated()) {
return res.redirect('/auth/login');
} else {
res.status(403);
return res.json({
error: 'Invalid Api Key'
});
}
}
So essentially, pass an API key via header and modify the logic in isAuthenticated function. So whenever the API key is available, it will call the next() callback.

Related

how to secure routes in API and in client with next-auth

I run an backend and a frontend both served by express the backend on port 8080 and the frontend on port 80.
/api/route1 returns 200ok with json
/api/route2 returns 200ok with json
So the app works fine fetching these routes. Now to the thing I need your help with. I have added next-auth so in the frontend I can
const [ session, loading ] = useSession();
to do something like
{!session && <p>You are not logged in</p>}
which works but what I haven't figured out is how to protect the routes to the API. I want to protect route1 and route2 in both frontend and backend. I guess when I'm logged in a need to pass a token down to the API but how can I have these 2 talking to each other
/api/route1 returns 200ok with json
/api/route2 returns 200ok with json
Remember I run the backend and frontend separately because my production build is in docker that's why.
You can find an example of this in the next-auth-example project
// pages/api/examples/protected.js
import { getSession } from 'next-auth/client'
export default async (req, res) => {
const session = await getSession({ req })
if (session) {
res.send({ content: 'This is protected content. You can access this content because you are signed in.' })
} else {
res.send({ error: 'You must be sign in to view the protected content on this page.' })
}
}
If a session object exists (i.e. is not null) then it means they either have a valid session token (if using database sessions) or a valid signed JSON Web Token (if using JWT session).
In both cases the session token is checked to make sure it is valid and has not expired.
The request object req is passed through to getSession() call when used in this way so that the cookie containing the session token can be inspected and validated.
The way that you could handle protected routes within Node is by using middleware.
So lets say that you have a route for adding employees salary in database, so obviously such a route needs someone that is and authenticated admin right?
So you could make a middleware function like the simple one below
const validateAdminCookie = (req, res, next)=>{
//Here you then write all your logic on how you validate admin
//Now you will have conditonals here that:
if (!validatedCookie){
return res.status(400).json({msg:'Not authorized'})
}
next();
}
So now that function is what you will pass within your route so that is gets executed first and when user is valid authenticated admin then the next() will push down that user to the main route that they were trying to hit else if not authenticated then the get back a message that they are not authenticated.
Now how you pass this middleware is like this below:
router.post('/api/admin-update-salaries',validateAdminCookie, (req, res)=>{
//Now that **validateAdminCookie** will execute first and if all
//checks out then user will be pushed down to the main part
//that is this route here
})

Response to preflight request doesn't pass access control check: It does not have HTTP ok status. GET working POST PUT DELETE not working

Greetings
I have one web application with following architecture:
Web api: ASP.net core 2.1 (Windows Authentication)
UI: angular 8
UI is able to get data but unable to send data.
I mean GET method is working fine but POST, PUT, DELETE options are not working .
And all the methods are working using POSTMAN.
ERROR is:
Access to XMLHttpRequest at 'http://xx.xxx.xxx.xx:xxyy/xxx/xxxxxx/Method' from origin 'http://localhost:xxxx' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Any help will be appreciated .
Thanks in advance :)
That's because your API is on different domain than your SPA angular application.
Please at this at the start of your Configure method in Startup.cs
if (env.IsDevelopment())
{
app.UseCors(opts =>
{
opts.WithOrigins(new string[]
{
"http://localhost:3000",
"http://localhost:3001"
// whatever domain/port u are using
});
opts.AllowAnyHeader();
opts.AllowAnyMethod();
opts.AllowCredentials();
});
}
Please note that this will handle only CORS for local development since you'll probably have same domain in production - if not, you'll need to reconfigure this for production also.
CORS blocking is browser specific and that's why it's working in PostMan but not in browser.
This is what i use and it should work i hope for your case.
My startup.cs ConfigureServices() decorated with:
services.AddCors(feature =>
feature.AddPolicy(
"CorsPolicy",
apiPolicy => apiPolicy
//.AllowAnyOrigin()
//.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
.SetIsOriginAllowed(host => true)
.AllowCredentials()
));
And, Configure() method with:
app.UseCors("CorsPolicy");
Notice the SetIsOriginAllowed() and allowCreds() along with other policy settings, this works for me with POST calls to my api from my angular, which are running on two different port#s.
UPDATE:
Following the questions on the comments, adding additional information on how do we check the logged in user (windows auth) btwn api and the angular (frontend).
You can check the incoming User on a specific route that would only expect the authenticated user using the decoration [Authorize]. In my case, i would have only one method that would expect the windows user in the api:
[HttpGet("UserInfo")]
[Authorize]
public IActionResult GetUserInfo()
{
string defaultCxtUser = HttpContext?.User?.Identity?.Name;
if (defaultCxtUser != null && !string.IsNullOrEmpty(defaultCxtUser))
{
_logger.LogDebug($"START - Get Context user details for {defaultCxtUser}");
ADHelper.logger = _logger;
var userFullName = ADHelper.GetUserIdentityInfo(defaultCxtUser);
_logger.LogInformation($"Context user {defaultCxtUser} with name: {userFullName}");
var userInfo = new { Name = userFullName };
//_logger.LogDebug($"END - GetUserInfo({defaultCxtUser} for {userFullName}");
return Ok(userInfo);
}
else
return Ok(new { Name = defaultCxtUser });
}
then i would call this from my angular with the service call as,
// Get the Logged in user info
GetCurrentUserInfo(): Observable<string> {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
}),
withCredentials: true
};
// return this.http.get<string>(`${ApiPath}UserInfo`, httpOptions)
// .pipe(map(v => v as string));
return this.http.get<UserInfo>(`${ApiPath}UserInfo`, httpOptions)
.pipe(map(data => {
// console.log(data, data.Name);
return data.Name;
}))
;
}
Please see the headers with 'withCredentials: true' line that would trigger to pass the current user info, and it would be read and understood only if it has the authorize attr to read the User.Identity object in c# side. The reason we do this on a specific method is that, there should be some other parental method in the api like ApiStatus() or anything that could be, should be called first. This would ensure to also invoke the preflight check with OPTIONS that would require anonymous auth. Like in my case, getting whether the api is available and running, and some other app environment info before i get the userInfo() from my angular app.

(graphql, prisma) How can i ingnore(pass) check auth when login(signin)?

I have a problem creating graphql server and checking auth. I automatically created schemas with Prisma, and I manually added to the schema by creating a 'signin' mutation. I have also added the jwt checking (auth) middleware, as shown in the following code:
server.express.post(
server.options.endpoint,
auth,
(err, req, res, next) => {
console.log('bb');
if (err) return res.status(401).send(err.message)
next()
}
)
The problem is that the token is checked even when the signin is done.
Is there a way not to confirm tokens when requesting signin interaction, or if so, how do I get over it?
(is it right that there isn't token when client doesn't signed in?)
In your auth middleware, you can access the Request (req). You could check to see which operation if called.
If you are calling the login operation, call next()
Otherwise, check the token

Graphql #include with expression

I am implementing a query which should serve some fields in the response depending on user login status.
To be specific, I want to get "pointRate" field only if $authenticationToken is passed & would want to avoid passing $authenticated in below query. The reason I want to avoid sending $authenticated is client can do mistake by sending $authenticated = true but $authenticationToken = null.
query ToDoQuery($authenticationToken: String, $authenticated: Boolean!) {
pointRate(accessToken: $authenticationToken) #include(if: $authenticated) {
status
}
}
So, Actually you want to do that
i) if $authenticationToken is passed, you want to get "pointRate".
ii) and you also want to avoid passing $authenticated in subsequent
queries. Because you are concern about your clients who can make some
mistake like sending authenticated is true where authentication token
was null.
So in generally I want to answer that if you want to handle authentication by yourself using GraphQL, at first you have to create a token, then you have to pass the token in every request or with subsequent requests. Otherwise it is not possible. Because sensitive data's will not be provided without authentication.
On the other hand, you can use session auth. You can access every data until session is closed.
If it is not satisfactory, You can read the following brief description with a scenerio like yours. I also tried to accumulate some related sample solutions for better understanding, it may clarify you more.
As GraphQL API is completely public, you can make authentication by two ways.
Let the web server (e.g. express or nginx) take care of authentication.
Handle authentication in GraphQL itself.
If you do authentication in the web server, you can use a standard auth package (e.g. passport.js for express) and many existing authentication methods will work out of the box. You can also add and remove methods at your liking without modifying the GraphQL schema.
If you’re implementing authentication yourself, do the followings
Make sure to never store passwords in clear text or a MD5 or SHA-256
hash
Use something like bcrypt
Make sure to not store your session tokens as-is on the server, you
should hash them first
You can write a login method, which sets the context. Since mutations
are executed one after the other and not in parallel, you can be sure
the context is set after the login mutation:
mutation {
loginWithToken(token: "6e37a03e-9ee4-42fd-912d-3f67d2d0d852"),
do_stuff(greeting: "Hello", name: "Tom"),
do_more_stuff(submarine_color: "Yellow")
}
Instead of passing in the token via header or query parameter (like JWT, OAuth, etc), we make it part of the GraphQL query. Your schema code can parse the token directly using the JWT library itself or another tool.
Remember to always use HTTPS when passing sensitive information :)
As parallel execution is an important for performance. and mutation and queries are executed serially, in the order given.
So in most cases It is preferred to handle authentication in the web server. It’s not only more generic, but also more flexible.
Scenerio:
First go through the followings
import jwt from'express-jwt';
import graphqlHTTP from'express-graphql';
import express from'express';
import schema from'./mySchema';
const app = express();
app.use('/graphql', jwt({
secret: 'shhhhhhared-secret',
requestProperty: 'auth',
credentialsRequired: false,
}));
app.use('/graphql', function(req, res, done) {
const user = db.User.get(req.auth.sub);
req.context = {
user: user,
}
done();
});
app.use('/graphql', graphqlHTTP(req => ({
schema: schema,
context: req.context,
})
));
If you check in the above section, you will get that API is not secure at all. It might try to verify the JWT but if the JWT doesn’t exist or is invalid, the request will still pass through (see credentialsRequired: false). Why? We have to allow the request to pass through because if we blocked it we would block the entire API. That means, our users wouldn’t even be able to call a loginUser mutation to get a token to authenticate themselves.
Solution#1:
Barebone example using Authenticate resolvers, not endpoints.
import { GraphQLSchema } from 'graphql';
import { Registry } from 'graphql-helpers';
// The registry wraps graphql-js and is more concise
const registry = new Registry();
registry.createType(`
type User {
id: ID!
username: String!
}
`;
registry.createType(`
type Query {
me: User
}
`, {
me: (parent, args, context, info) => {
if (context.user) {
return context.user;
}
throw new Error('User is not logged in (or authenticated).');
},
};
const schema = new GraphQLSchema({
query: registry.getType('Query'),
});
By the time the request gets to our Query.me resolver, the server middleware has already tried to authenticate the user and fetch the user object from the database. In our resolver, we can then check the graphql context for the user (we set the context in our server.js file) and if one exists then return it else throw an error.
Note: you could just as easily return null instead of throwing an error and I would actually recommend it.
Solution#2:
Use functional Composition(middleware based) of express-graphql
import { GraphQLSchema } from 'graphql';
import { Registry } from 'graphql-helpers';
// See an implementation of compose https://gist.github.com/mlp5ab/f5cdee0fe7d5ed4e6a2be348b81eac12
import { compose } from './compose';
const registry = new Registry();
/**
* The authenticated function checks for a user and calls the next function in the composition if
* one exists. If no user exists in the context then an error is thrown.
*/
const authenticated =
(fn: GraphQLFieldResolver) =>
(parent, args, context, info) => {
if (context.user) {
return fn(parent, args, context, info);
}
throw new Error('User is not authenticated');
};
/*
* getLoggedInUser returns the logged in user from the context.
*/
const getLoggedInUser = (parent, args, context, info) => context.user;
registry.createType(`
type User {
id: ID!
username: String!
}
`;
registry.createType(`
type Query {
me: User
}
`, {
me: compose(authenticated)(getLoggedInUser)
};
const schema = new GraphQLSchema({
query: registry.getType('Query'),
});
The above code will work exactly the same as the first snippet. Instead of checking for the user in our main resolver function, we have created a highly reusable and testable middleware function that achieves the same thing. The immediate impact of this design may not be apparent yet but think about what would happen if we wanted to add another protected route as well as log our resolver running times. With our new design its as simple as:
const traceResolve =
(fn: GraphQLFieldResolver) =>
async (obj: any, args: any, context: any, info: any) => {
const start = new Date().getTime();
const result = await fn(obj, args, context, info);
const end = new Date().getTime();
console.log(`Resolver took ${end - start} ms`);
return result;
};
registry.createType(`
type Query {
me: User
otherSecretData: SecretData
}
`, {
me: compose(traceResolve, authenticated)(getLoggedInUser)
otherSecretData: compose(traceResolve, authenticated)(getSecretData)
};
Using this technique will help you build more robust GraphQL APIs. Function composition is a great solution for authentication tasks but you can also use it for logging resolvers, cleaning input, massaging output, and much more.
Solution#3:
A decent solution is to factor out data fetching into a separate layer and do the authorization check there.
Below is an example that follows the principles outlined above. It’s for a query that fetches all todo lists that a user can see.
For the following query,
{
allLists {
name
}
}
Don’t do this:
//in schema.js (just the essential bits)
allLists: {
resolve: (root, _, ctx) => {
return sql.raw("SELECT * FROM lists WHERE owner_id is NULL or owner_id = %s", ctx.user_id);
}
}
Instead, I suggest you do this:
// in schema.js (just the essential bits)
allLists: {
resolve: (root, _, ctx) => {
//factor out data fetching
return DB.Lists.all(ctx.user_id)
.then( lists => {
//enforce auth on each node
return lists.map(auth.List.enforce_read_perm(ctx.user_id) );
});
}
}
//in DB.js
export const DB = {
Lists: {
all: (user_id) => {
return sql.raw("SELECT id FROM lists WHERE owner_id is NULL or owner_id = %s, user_id);
}
}
}
//in auth.js
export const auth = {
List: {
enforce_read_perm: (user_id) => {
return (list) => {
if(list.owner_id !== null && list.owner_id !== user_id){
throw new Error("User not authorized to read list");
} else {
return list;
}
}
}
}
You may think that the DB.Lists.all function is already enforcing permissions, but the way I see it it’s just trying not to fetch too much data, the permissions themselves are enforced not on each node separately. That way you have the auth checks in one place and can be sure that they will be applied consistently, even if you fetch data in many different places.
Solution#4:
Auth flow can be done in many different ways.
i) basic auth,
ii) session auth, or
iii) token auth.
As your issue is according to token auth, I would like to meet you with Scaphold which one uses token authentication. Everything we do, whether it be logging a user into Scaphold or logging your user into your app, we use tokens to manage a user's auth status. The auth flow works like this:
a) User logs in with username and password.
b) The GraphQL server verifies the user in the database against his / her hashed password.
c) If successful, the server returns a JSON Web Token (JWT) that is a Base64 encoded token with an expiration date. This is the authentication token.
d) To use the authentication token, your future requests should include the authentication token in the header as
{ Authorization: 'Bearer' + [Auth_Token] }
Now, each time the server (perhaps Node Express) sees the token in the header, it will parse out the token, verify it, and in the GraphQL world, save the identified user in the context for use in the rest of the application. The user is now logged in.
For more, you can learn more about #include in this tutorial: https://github.com/mugli/learning-graphql/blob/master/4.%20Querying%20with%20Directives.md#include
For learning step by step graphql authentication, you can go through this tutorial: GraphQL Authentication
Resource Link:
Authentication with
GraphQL
A guide to authentication in
GraphQL
Best practices for GraphQL
security
I don't think this is possible since you cannot convert an (empty) String to a Boolean in GraphQL.
Also, some advice from the official GraphQL docs:
Delegate authorization logic to the business logic layer
#include
GraphQL queries are a powerful way to declare data in your application.
The include directive, allows us to include fields based on some condition.
query myAwesomeQuery($isAwesome: Boolean) {
awesomeField #include(if: $isAwesome)
}
Note. #skip always has higher precedence than #include.

KeystoneJS signin

I am creating a keystone project and I need to provide allow or not allow users to access using the keystone signin. However, I found that keystoneJS sends a form data with email, password and csrf. This csrf is given to the user when he access to the login page.
Nevertheless, what I need to do is to comunicate externally to login the user, by using an API. How can I generate the _csrf? Is there another way then generate two requests?
Thanks
#Sericaia, you didn't include any code or specifics on how you intend to implement your login page, so my answer will be a little vague.
Keystone has an internal API for handling CSRF token creation and validation. I don't think it's documented, but here's a gist of how it works.
In your route handler you can create a CSRF token key/value pair that you can then inject into your view locals and then use in your view template. You can do it manually like this.
app.get('/login', function (req, res) {
var keystone = require('keystone');
var csrfTokenKey = keystone.security.csrf.TOKEN_KEY;
var csrfTokenValue = keystone.security.csrf.getToken(req, res);
res.render('login', {
csrfTokenKey: csrfTokenKey,
csrfTokenValue: csrfTokenValue
});
});
Or you can use provided middleware.
// the middleware will automatically inject the CSRF token
// into res.locals[keystone.security.csrf.LOCAL_KEY]
app.get('/login', keystone.security.csrf.middleware.init, function(req, res) {
...
});
You can also validate the CSRF token received from the client. You can do it manually as follows:
app.post('/login', function(req, res) {
if (keystone.security.csrf.validate(req)) {
// CSRF is valid
...
} else {
// CSRF is not valid
...
}
});
Or you can use the provided middleware.
// the middleware will return 403 status with "CSRF token mismatch"
// of there's a error validating the CSRF token received
app.post('/login', keystone.security.csrf.middleware.validate, function(req, res) {
...
});
Hope this helps.