Authentication in GraphQL servers - authentication

How to properly handle authentication in GraphQL servers?
Is it ok to pass a JWT token at the Authorization header of query/mutation requests?
Should I use something from GraphQL specification?
Stateless solutions is preferable.
Thanks.

A while ago I was wondering the same thing for sometime,
but apparently authentication is out of the scope of what GraphQL is trying to accomplish (see the conversations on Github).
But there are solutions such as this which handles it with sessions.
Assuming you use express-graphql, here is what you can do.
import graphQLHTTP from 'express-graphql'
app.use(`/graphql`, [aValidationFunction, graphQLHTTP(options)])
function aValidationFunction(req, res, next) {
const { authorization } = req.headers
// Do your validation here by using redis or whatever
if (validUser) {
return next()
} else {
return res.status(403)
}
}

It depends on whether your GraphQL consumer is a webapp or mobileapp.
If it is a webapp, then I would recommend sticking with session-cookie-based authentication since most popular web frameworks support this, and you also get CSRF protection.
If it is a mobileapp, then you will want JWT. You can try manually getting a cookie header from login response, and put stuff this "cookie" in your next request, but I had problem that some proxy servers strip off this "cookie", leaving your request unauthenticated. So as you said, including JWT in every authenticated request (GraphQL request) is the way to go.

Related

Ktor Keycloak Acess Token Route Protection

I'm using a server that authenticates through keycloak. In keycloak I have created a realm and am able to get an Access token as a response. This access token gets fed now into my Ktor application.
However, I'm not quite sure how to protect routes in an easy manner. I want to have some protected routes that have a authenticate("keycloakOAuth"){} scope around it which handles validating the access token and refreshing using the refresh token if the access token is expired.
Currently I have keycloak inside Ktor configured as this:
authenticate("keycloakOAuth") {
get("login") {}
route("/callback") {
// This handler will be executed after making a request to a provider's token URL.
handle {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal != null) {
val response = principal as OAuthAccessTokenResponse.OAuth2
call.respondText { "Access token: ${response.accessToken}" }
} else {
call.respondText { "NO principal" }
}
}
}
}
This works fine because when I go to login I'm getting sent to the Keycloak login page and I can login. When I logged in the callback executes and I get my Access Token back.
When I'm trying to protect routes however, some odd stuff happens. I know that I need to validate the incoming JWT token. But I have no clue how to given the Ktor capabilities. The examples are also of little help, since they are quite vague.
Currently I have something like this:
authenticate("keycloakOAuth") {
get("/testAuth") {
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
if(principal != null) {
call.respondText("Authenticated!")
} else {
call.respondText("Unauthenticated...")
}
}
}
But my application will always send me to the login page and then callback page, even though I am sending the Bearer token when I'm testing this call.
My question is:
How do I protect routes in a manner that they need a valid token, with the same syntax that Ktor uses (like authenticate(){}). Do I need to configure JWT for this?
When you request one of the routes under authenticate, the full cycle of OAuth authentication is triggered. This is because the Authentication plugin is designed so a client sends credentials and gets authenticated for each request. For some reason, OAuth integration was implemented on top of the Authentication plugin hence such unexpected behavior.
To solve your problem you can have only /login and /callback routes restricted. In the callback's handler save user ID and tokens in a session or in any other storage for future use. For other routes, you can check manually the fact that a user is authenticated and then use tokens from storage to acquire protected data from the resource server. For convenience, you can create some extension functions to minimize the amount of boilerplate code. Unfortunately, there is no built-in functionality to make it work out of the box.
You don't need to configure JWT for this.

Using OICD Authorization Code Flow with a RESTful API and an SPA

I'm currently working on a personal project which involves a Quarkus REST API as a back-end, Keycloak as OpenId Connect Provider and a Vue app as front-end. I just can't wrap my head around how to make these three components play well together for user authentication while maintaining proper security.
According to draft v8 of OAuth 2.0 for Browser-Based Apps, the SPA shouldn't be the one to keep the access and (possibly) refresh tokens because they are hard to store securely in such a scenario. That means, the back-end must be acting as the Relying Party, initiating the OIDC Authorization Code Flow. I'd then either keep a session cookie with my SPA (which I'd prefer not to do, to keep the API stateless), or store the tokens inside a Secure, SameSite, HttpOnly cookie.
The approach is what I’m trying to accomplish, so far with little success.
Prototype implementation
My Quarkus app uses the quarkus-oicd extension.
The way I understand it, I have to add the following configuration to Quarkus' application.properties:
quarkus.oidc.application-type=web-app
quarkus.oidc.client-id=myClientId
quarkus.oidc.credentials.secret=********
quarkus.oidc.auth-server-url=http://127.0.0.1:8082/auth/realms/myRealm
The application-type=web-app being what tells Quarkus that it is responsible for initiating the authorization code flow. The alternative would be service, in which case Quarkus only validates bearer tokens the client sends to the API.
The API is running on port 8081 and only exposes a single sample resource:
#Path("/hello")
#Authenticated
public class ReactiveGreetingResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello RESTEasy Reactive";
}
}
This simple Vue component is meant as a proof-of-concept:
// FetchComponent.vue
<template>
<div>
<button v-on:click="fetchFromBackend">Fetch</button>
<p><b>Output:</b>{{ message }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
data() {
return {
message: "Click the button to fetch.",
};
},
methods: {
fetchFromBackend(): void {
this.message = "Waiting...";
fetch("http://localhost:8081/hello", {
credentials: "include",
})
.then((resp) => {
console.log(resp);
if (resp.redirected) {
window.location.assign(resp.url);
} else {
return resp.text().then((text) => (this.message = text));
}
})
.catch((reason) => (this.message = "Caught error: " + reason));
},
},
});
</script>
<style></style>
Desired outcome
I'd have expected that the call to the back-end without a valid token gets redirected to Keycloak's authentication page. The user would then enter their credentials, be logged in and redirected back to the SPA with an auth code. It calls the API again. On the back-channel, Quarkus exchanges the auth code against a token and forwards it to the client in form of a cookie. This cookie would be used to authenticate the user for any further API calls.
Actual outcome
When the back-end is called without a valid token, it redirects to Keycloak's login page, as expected. Apparently, though, there is simply no way to navigate to the redirected URL from JS. The fetch specification states: "Redirects (a response whose status or internal response’s (if any) status is a redirect status) are not exposed to APIs. Exposing redirects might leak information not otherwise available through a cross-site scripting attack." The redirect: 'manual' option in a fetch request is somewhat of a red herring. It doesn't do what one would expect and it certainly doesn't allow me access to the redirect URL.
What happens instead is that the browser transparently follows the redirect and tries to fetch the login URL. That doesn't work at all. It results in a CORS error, because Keycloak doesn't set the relevant headers (and I suppose it shouldn't, because this isn't how it's supposed to work).
I have no clue how to proceed from here but I presume that the answer is extremely obvious to more experienced people.
As a closing remark I'd like to add that this architecture wasn't the result of a very well-informed decision making process. I chose it mostly because:
Quarkus: Java is currently my primary language at my job
Keycloak: I wanted to try my hand at proper externalized IAM and SSO for a while now. This seemed a good opportunity.
Vue: I wanted something to train my JS skills and which would look good on my resume. Any of the current batch of hot SPA frameworks would have fit the bill.
So, any answers along the lines of "that's a terrible setup, just don't do it, try X instead" are definitely also welcome, even though I'd still love to solve this puzzle as a matter of pride.

Is there any way to block HTTP requests made by Postman in .NET Core?

I just wanted to know whether is there any way block to HTTP requests made by POSTMAN? Just like browser with the help of CORS allows only specific origins to access a resource. Thanks in advance.
No.
In CORS, it's browser job to block request (or answer), your server does not know "truth" about request. If some power user will disable "following CORS rules" in browser settings/flags - your CORS settings will be ignored.
And even if you will find some "special headers" that POSTMAN will "understand" and refuse to work - there are many other "clients" that can send http(s) requests to server (curl, Fiddler, ...).
I am not aware of anything that gives away the fact that the request is made via Postman.
At the end of the day, Postman is a simple client so the fact that the request is coming through it, or any other client as a matter of fact is irrelevant. Postman's job is to help talk to APIs and even automate this process.
If you are worried about security then secure your API first. Then you wouldn't really care how you get a request, as long as it's authenticated and actually allowed to talk to your API.
This is maybe old for this question but one of the easiest way to handle such situation is to
app.Use(async (context, next) =>
{
if (context.Request.Headers["Referer"].ToString() != "http://localhost:4200/")
{
byte[] data = Encoding.ASCII.GetBytes("Not Recognized Request");
await context.Response.Body.WriteAsync(data);
return;
}
await next();
});
This is useful for .Net core and must set in startup--> configure section.
via this approach you will restrict your API to "Http://localhost:4200" which would be the "Referer" that you want to restrict to.
So because postman has no "Referer" it will get "Not Recognized request" as response.

Ember.js REST Auth Headers

I've been struggling with this for too long now. I have an Expressjs server that provides an endpoint to login. The response I get has a JWT token, expiring in an hour. All good.
On the Emberjs side, I can successfully authenticate and get the token (using ember-simple-auth and ember-simple-auth-token). This works well for protecting my routes. But I can't for the life of me update the RESTAdapter headers to include my new authorization token.
I've tried:
using $.ajaxPrefilter to set the Authorization header. Didn't work
accessing "this.get('session.secure.token')" from the RESTAdapter. Thats undefined.
Please, if someone could point me in the right direction, I'd be eternally grateful. All I need to do is attach the value in "session.secure.token" to the header for all RESTAdapter requests.
Thanks
You should be able to set the simple-auth config property authorizer to simple-auth-authorizer:token - in the simple-auth code it looks for this config property, looks up simple-auth-authorizer:token and uses this in combination with ajaxPrefilter.
// config/environment.js
ENV['simple-auth'] = {
authorizer: 'simple-auth-authorizer:token'
};

how do you request a session from servicestack basic authentication, at /auth/basic?

I have set up a servicestack service with basic authentication using the first example, here:
https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorization
This automatically sets up a route: /auth/basic
However, I cannot find any information or examples on how to format a request to this URL (Variables/GET/POST/Auth Header, etc.).
I am able to access a simple service using the basic authentication credentials, so they are active and correct.
I have no custom authentication plugged in, just basic authentication.
I have tried:
Using a JsonServiceClient to send UserName and Password variables by GET or Json POST to /auth/basic, with and without an Auth header also containing the user & pass.
Using a browser to send GET requests with URL parameters of the user/pass, or as http://user:pass#localhost:123/auth/basic
I always just get "HTTP/1.1 401 Invalid BasicAuth credentials".
The only examples I can find involve some kind of custom authentication, and then /auth/credentials is accessed, but I want to use /auth/basic
I have looked at the code and it looks like it reads an Auth header, but the service does not accept one.
I am actually trying to get this working so I can then disable it and verify it is disabled (I want to require basic authentication for every request).
Questions are:
What is the correct way to call the /auth/basic service? I will take a servicestack client API example, specifications or even a raw http request!
How do you disable the /auth services altogether?
Many thanks.
What is the correct way to call the /auth/basic service? I will take a servicestack client API example, specifications or even a raw http request!
var client = new JsonServiceClient("http://localhost:56006/api");
var resp = client.Post(new Auth() { UserName = "TestUser", Password = "Password" });
This assumes you have also registered an ICacheClient and IAuthUserRepository (and added a user account)
The JSON format looks like this if you call into /auth/basic?format=json
{
"UserName": "admin",
"Password": "test"
"RememberMe": true
}
How do you disable the /auth services altogether?
Don't add the AuthFeature plugin to configuration.
You can also remove plugins
Plugins.RemoveAll(x => x is AuthFeature);
Putting the following in apphost config seems to do the trick.
//Disable most things, including SOAP support, /auth and /metadata routes
SetConfig(new EndpointHostConfig()
{
EnableFeatures = Feature.Json | Feature.Xml
});
I am a little suspicious about what this does to /auth however, because it returns an empty response, while most routes return 404.
So, would this truly disable the /auth functionality? As in, if someone formed a correct request to /auth/credentials, will it still return an empty response?