How to secure a REST API with an API key - api

I'm currently creating a Rest API with NestJS (it's really cool by the way).
In this API, I'm using JWT (Json Web Token) to allow users to log in and view different resources based on their role.
However, I want to implement an API Key system to protect the API itself. I don't want any developer to be able to use my api. I want him to go through this API Key to use my api.
Either by a query in the url : https://domaine.com?api_key=${API_KEY} or via the header :
GET /v1/some-resource
Host: docmaine.com
Accept: application/json
X-API-KEY: MyAwes0m3API_KeY
Do you have a tutorial, course or a track to advise me?

Why not create a guard that checks for the validity of that header?
#Injectable()
export class ApiKeyGuard implements CanActivate {
constructor(private readonly apiKeyService: ApiKeyService) {} // made up service for the point of the exmaple
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
const key = req.headers['X-API-KEY'] ?? req.query.api_key; // checks the header, moves to query if null
return this.apiKeyService.isKeyValid(key);
}
}
And now you can use #UseGuards(ApiKeyGuard) on whatever route, or bind it globally, and you've got some basic authentication up for your server.

Related

implement authentication in Next.js, graphQL, Apollo client

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.

What is Code Сhallenge in query param in authorization server like IdentityServer (from JS SPA client pov)?

When I do manual redirect, I'm getting an error from IdentityServer
invalid_request, code challenge required
However when I use oidc-client-js library for the same authorization request, I do not get that error. Library somehow sets code challenge under the hood.
Here is me JS code.
Set up:
const config = {
authority: "https://demo.identityserver.io",
client_id: "interactive.confidential",
redirect_uri: "http://localhost:3000/callback",
response_type: "code",
scope:"openid profile email api offline_access",
post_logout_redirect_uri : "http://localhost:3000/post_logout",
};
const url = `https://demo.identityserver.io/connect/authorize?
client_id=${config.client_id}&
redirect_uri=${config.redirect_uri}&
response_type=${config.response_type}&
scope=${config.scope}`;
My manual authorization redirect request that throws:
const onFormSubmit = async (ev: React.FormEvent) => {
ev.preventDefault();
window.location.replace(url); // I simply do replace
}
Code with the library that doesn't throw:
import Oidc from 'oidc-client';
const onFormSubmit = async (ev: React.FormEvent) => {
ev.preventDefault();
const mgr = new Oidc.UserManager(config);
mgr.signinRedirect(); // login redirect here, no errors
}
I want to understand what code challengem is. And how it gets generated. Give me a hint what to read about it.
I ca go on with the library, but I'd prefer not to import third-party libs into my app where possible.
Authorize Endpoint handle multiple grant types, the way you are sending your request, matched to Authorization Code Grant which needs code_challenge parameter during the request.
Try something simpler to make a request like:
GET /connect/authorize?
client_id=client1&
scope=openid email api1&
response_type=id_token token&
redirect_uri=https://myapp/callback&
state=abc&
nonce=xyz
Read Authorize Endpoint for more information.
Heres an example of generating a challenge code:
private string CreateCodeChallenge()
{
_codeVerifier = RandomNumberGenerator.CreateUniqueId();
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
var challengeBuffer = sha256.HashData(
CryptographicBuffer.CreateFromByteArray(Encoding.UTF8.GetBytes(_codeVerifier)));
byte[] challengeBytes;
CryptographicBuffer.CopyToByteArray(challengeBuffer, out challengeBytes);
return Base64Url.Encode(challengeBytes);
}
Include the code and the method in the request querystring.
You can generate codes for testing here: https://tonyxu-io.github.io/pkce-generator/
That's as far I've gotten with it but I am shown the login screen.
It's a parameter required by the Proof Key for Code Exchange standard.
OAuth 2.0 public clients utilizing the Authorization Code Grant are susceptible to the authorization code interception attack. This specification describes the attack as well as a technique to mitigate against the threat through the use of Proof Key for Code Exchange (PKCE, pronounced "pixy").

Ember authentication with Oauth server/client

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.

ember simple auth session, ember data, and passing a Authorization header

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

Alternative to cookie based session/authentication

Is there an alternative to the session feature plugin in servicestack? In some scenarios I cannot use cookies to match the authorized session in my service implementation. Is there a possibility to resolve the session using a token in http header of the request? What is the preferred solution for that in case the browser is blocking cookies?
I'm using ServiceStack without the built-in auth and session providers.
I use a attribute as request filter to collect the user information (id and token), either from a cookie, request header or string parameter.
You can provide this information after the user takes login. You append a new cookie to the response and inject the id and token info on clientside when rendering the view, so you can use for http headers and query parameters for links.
public class AuthenticationAttribute : Attribute, IHasRequestFilter
{
public void RequestFilter(IHttpRequest request, IHttpResponse response, object dto)
{
var userAuth = new UserAuth { };
if(!string.IsNullOrWhiteSpace(request.GetCookieValue("auth"))
{
userAuth = (UserAuth)request.GetCookieValue("auth");
}
else if (!string.IsNullOrEmpty(request.Headers.Get("auth-key")) &&
!string.IsNullOrEmpty(request.Headers.Get("auth-id")))
{
userAuth.Id = request.Headers.Get("id");
userAuth.Token = request.Headers.Get("token");
}
authenticationService.Authenticate(userAuth.Id, userAuth.token);
}
public IHasRequestFilter Copy()
{
return new AuthenticationAttribute();
}
public int Priority { get { return -3; } } // negative are executed before global requests
}
If the user isn't authorized, i redirect him at this point.
My project supports SPA. If the user consumes the API with xmlhttprequests, the authentication stuff is done with headers. I inject that information on AngularJS when the page is loaded, and reuse it on all request (partial views, api consuming, etc). ServiceStack is powerful for this type of stuff, you can easily configure your AngularJS app and ServiceStack view engine to work side by side, validating every requests, globalizing your app, etc.
In case you don't have cookies and the requests aren't called by javascript, you can support the authentication without cookies if you always generate the links passing the id and token as query parameters, and pass them through hidden input on forms, for example.
#Guilherme Cardoso: In my current solution I am using a PreRequestFilters and the built-in session feature.
My workflow/workaround is the following:
When the user gets authorized I took the cookie and send it to the client by using an http header. Now the client can call services if the cookie is set in a http-header (Authorization) of the request.
To achieve this I redirect the faked authorization header to the cookie of the request using a PreRequestFilter. Now I am able to use the session feature. Feels like a hack but works for the moment ;-)
public class CookieRestoreFromAuthorizationHeaderPlugin : IPlugin
{
public void Register(IAppHost appHost)
{
appHost.PreRequestFilters.Add((req, res) =>
{
var cookieValue = req.GetCookieValue("ss-id");
if(!string.IsNullOrEmpty(cookieValue))
return;
var authorizationHeader = req.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.ToLower().StartsWith("basictoken "))
{
var cookie = Encoding.UTF8.GetString(Convert.FromBase64String(authorizationHeader.Split(' ').Last()));
req.Cookies.Add("ss-id",new Cookie("ss-id",cookie));
req.Items.Add("ss-id",cookie);
}
});
}
}