loopback 4 interceptor with metadata Similar to authorize - loopbackjs

I want to restrict API access with an access-key how most of the APIs provide read access to anonymous users. In my use case my frontend isn't guarded with JWT or any token. I won't to prevent any random person post to my API.
I want to give my frontend read access to come endpoint while write access to other. besides frontend I will have other API connecting to my API with different access key which allows read to some end points and write to other.
I have this interceptor
import {
injectable,
Interceptor,
InvocationContext,
InvocationResult,
Provider,
ValueOrPromise
} from '#loopback/core';
/**
* This class will be bound to the application as an `Interceptor` during
* `boot`
*/
#injectable({tags: {key: AccessKeyRestrictionInterceptor.BINDING_KEY}})
export class AccessKeyRestrictionInterceptor implements Provider<Interceptor> {
static readonly BINDING_KEY = `interceptors.${AccessKeyRestrictionInterceptor.name}`;
/*
constructor() {}
*/
/**
* This method is used by LoopBack context to produce an interceptor function
* for the binding.
*
* #returns An interceptor function
*/
value() {
return this.intercept.bind(this);
}
/**
* The logic to intercept an invocation
* #param invocationCtx - Invocation context
* #param next - A function to invoke next interceptor or the target method
*/
async intercept(
invocationCtx: InvocationContext,
next: () => ValueOrPromise<InvocationResult>,
) {
try {
// Add pre-invocation logic here
console.log('invocationCtx.methodName ', invocationCtx.methodName)
console.log('invocationCtx.args[0] ', invocationCtx.args[0])
console.log('invocationCtx.target ', invocationCtx.target)
console.log('pre-invocation', invocationCtx)
const result = await next();
// Add post-invocation logic here
return result;
} catch (err) {
// Add error handling logic here
throw err;
}
}
}
and using it in my controller
#intercept('interceptors.accessKey')
#post('/only-with-access-key')
I want to add some metadata while calling interceptor something like #intercept('interceptors.accessKey', {metadata: { resource: 'Device', scopes: ['read'] }}).
I know Loopback 4 provides special interceptor #authorize but I don't belive I can use it without #authenticate('jwt'). My use case do not have room for authentication. How can I achieve this?

Related

Location of auth:api Middleware

Can somebody please tell the location of auth:api middleware?
As per the auth:api middleware, the api routes are protected by not null users.
I have a boolean field in user table called Is_Admin_Url_Accessible. I want to add a condition in the auth:api middleware for some routes to make user that only those user access such routes whoever are permitted to access the admin area.
I checked the class here but could not help.
\app\Http\Middleware\Authenticate.php
You can add a middleware that makes control user accessible, and you can set it as middleware to your route group like auth:api
Please run php artisan make:middleware UserAccessible on your terminal.
After run above artisan command, you will see generated a file named UserAccessible.php in the App/Http/Middleware folder.
UserAccessible.php Contents
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class UserAccessible
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Auth::user();
if(!$user->accesible){
// redirect page or error.
}
return $next($request);
}
}
Then, you must define a route middleware via App/Http/Kernel.php
Kernel.php Contents
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* #var array
*/
protected $routeMiddleware = [
...
'user_accessible' => \App\Http\Middleware\UserAccessible::class
];
And finally, you can define route middleware to your route group;
api.php Contents
Route::group(['middleware' => ['auth:api', 'user_accessible']], function () {
// your protected routes.
});
I hope this solve your problem.

aws-sdk-js transact operations not exposed to DocumentClient?

I'm developing some business administration software in JS and I find myself in need of ACID transactions with DynamoDb. Lucky of mine, AWS just released the transactGet and transactWrite APIs that covers just the right use case!
I'm using the AWS.DynamoDb.DocumentClient object to make my other calls and it seems like the transact operations are not exposed for me to use.
I hacked inside the aws-sdk code and found the interface exports like such ("aws-sdk": "^2.368.0", document_client.d.js, line 2076):
export interface TransactWriteItem {
/**
* A request to perform a check item operation.
*/
ConditionCheck?: ConditionCheck;
/**
* A request to perform a PutItem operation.
*/
Put?: Put;
/**
* A request to perform a DeleteItem operation.
*/
Delete?: Delete;
/**
* A request to perform an UpdateItem operation.
*/
Update?: Update;
}
However, whenever I try to call the client with this methods, I get back a Type error, namely:
TypeError: dynamoDb[action] is not a function
Locally, I can just hack the sdk and expose everything, but it is not accepted on my deployment environment.
How should I proceed?
Thank you very much!
Edit:
If it is worth anything, there is the code I'm using to do the calls:
dynamo-lib.js:
import AWS from "aws-sdk";
export function call(action, params) {
const dynamoDb = new AWS.DynamoDB.DocumentClient();
return dynamoDb[action](params).promise();
}
Lambda code:
import * as dynamoDbLib from '../libs/dynamodb-lib';
import { success, failure } from '../libs/response-lib';
export default async function main(params, callback) {
try {
const result = await dynamoDbLib.call("transactWrite", params);
callback(null, success(result));
} catch (e) {
console.log(e);
callback(null, failure({"status": "Internal server error"}));
}
}
Edit 2:
Seems like the document client really does not export the method.
AWS.DynamoDB.DocumentClient = AWS.util.inherit({
/**
* #api private
*/
operations: {
batchGetItem: 'batchGet',
batchWriteItem: 'batchWrite',
putItem: 'put',
getItem: 'get',
deleteItem: 'delete',
updateItem: 'update',
scan: 'scan',
query: 'query'
},
...
As mentioned in accepted answer, the workaround is to use the transactWriteItems method straight from the DynamoDB object.
Thanks for the help! =D
Cheers!
UPDATE:
The issue has been resolved
Github Thread
AWS SDK is not currently supporting transactions in dynamodb document client i have raised the issue on github a current workaround is just not to use document client
let aws=require("aws-sdk");
aws.config.update(
{
region:"us-east-1",
endpoint: "dynamodb.us-east-1.amazonaws.com"
}
);
var dynamodb = new aws.DynamoDB();
dynamodb.transactWriteItems();
Also make sure you are using SDK v2.365.0 plus
Update the aws-sdk package (>2.365)
Use
var dynamodb = new aws.DynamoDB();
instead of
var dynamodb = new aws.DynamoDB.DocumentClient();

ServiceStack: Authenticate each request using headers in the HTTP request

I have have read other posts on the same topic, but I haven't really gotten a clear picture of how to best solve this:
I have a webservice, that is "stateless" when it comes to the authentication/session, meaning that the client will send two strings with every request (in the HTTP header), AuthToken and DeviceUUID.
These two strings are then compared to the storage, and if found, we know which user it is.
1)
Id like to use the [Authenticate] attribute for each service that I want to protect, and then a method should be executed where I check the two strings.
2)
If I add the [RequiredRole], a method should also be executed, where I have access to the HTTP headers (the two strings), so I can do my lookup.
I am unsure of how to do this in the easiest and cleanest manner possible. I do not want to create ServiceStack Session objects etc, I just want a method that, for each decorated services, runs a method to check authenticated state.
If you want to execute something else in addition when the [Authenticate] and [RequiredRole] attributes are used then it sounds like you want a custom [MyAuthenticate] Request Filter attribute which does both, i.e. validates that the request is Authenticated and executes your custom functionality, e.g:
public class MyAuthenticateAttribute : AuthenticateAttribute
{
public override async Task ExecuteAsync(IRequest req, IResponse res, object dto)
{
await base.ExecuteAsync(req, res, requestDto);
var authenticated = !res.IsClosed;
if (authenticated)
{
//...
}
}
}
Then use it instead of [Authenticate] in places where you need that extra functionality:
[MyAuthenticate]
public class MyServices { ... }
But I'd personally keep the logic in the attributes separated:
public class MyLogicPostAuthAttribute : RequestFilterAsyncAttribute
{
public override async Task ExecuteAsync(IRequest req, IResponse res, object dto)
{
//...
}
}
So they're explicit and can be layered independently of the [Authenticate] attribute, e.g:
[Authenticate]
[MyLogicPostAuth]
public class MyServices { ... }
Which can also be combined like:
[Authenticate, MyLogicPostAuth]
public class MyServices { ... }

Access API using client id and secret key in laravel/lumen

I have created an API for my web application. Now I want to give access to the world but before giving access I want mechanism something like Facebook API, Twitter API, Google API who provides client ID and Secret Key. Currently, I am using JWT AuthController, user login with his credentials and return a token, I don't want the users to be login.
I want the user can access my API using client ID and secret key? Another thing is that and How I will create client ID's and secret keys for the users?
Is this can be achieved using JWT Auth?
Any help?
I have read the article and quite promising it is, but after few post it recommends to use oauth2, here you go:
https://laracasts.com/discuss/channels/lumen/api-authorization-via-public-and-secret-keys
quotes:
Just add in the class to your API config.
namespace App\Providers\Guard;
use Dingo\Api\Auth\Provider\Authorization; use
Dingo\Api\Routing\Route; use Illuminate\Http\Request; use
Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class GuardProvider extends Authorization {
/**
* Get the providers authorization method.
*
* #return string
*/
public function getAuthorizationMethod()
{
return 'X-Authorization';
}
/**
* Authenticate the request and return the authenticated user instance.
*
* #param \Illuminate\Http\Request $request
* #param \Dingo\Api\Routing\Route $route
*
* #return mixed
*/
public function authenticate(Request $request, Route $route)
{
$key = $request->header(env('API_AUTH_HEADER', 'X-Authorization'));
if (empty($key)) $key = $request->input(env('API_AUTH_HEADER', 'X-Authorization'));
if (empty($key)) throw new UnauthorizedHttpException('Guard', 'The supplied API KEY is missing or an invalid authorization header was sent');
$user = app('db')->select("SELECT * FROM users WHERE users.key = ?", [$key]);
if (!$user) throw new UnauthorizedHttpException('Guard', 'The supplied API KEY is not valid');
return $user;
}
}

Servlet Filter as Security Proxy for Web Services

Good time.
Suppose there are 8 web-services in the one application. 5 of them require authorization (a client must to provide a JSESSIONID cookie and a corresponding session must not be invalidated), other 3 can be called without the jsessionid cookie. My naive solution is to write a servlet filter which intercepts requests and retrieve their pathInfos (all the services have the same url structure: /service/serviceSuffix). There is a enum which contains the serviceSuffix of each web service that requires authorization. When the request is retrieved the pathInfo is collected; if this pathInfo is contained in the enum and there is the corresponding valid session the request is sent ahead to the filter chain. Otherwise, an error is sent back to a client. After a while I've realized that it is needed to add the possibility to retrieve the wsdl and xsds for the concrete service. So that, two more check were added.
public class SecurityFilter implements Filter {
public static final String WSDL = "wsdl";
public static final String XSD = "xsd=";
/**
* Wittingly left empty
*/
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse)response;
String pathInfo = servletRequest.getPathInfo();
String queryString = servletRequest.getQueryString();
if (pathInfo != null && SecureWebServices.contains(pathInfo)) {
if (queryString != null && (queryString.equals(WSDL) || queryString.startsWith(XSD))) {
// wsdl or xsd is requested
chain.doFilter(request, response);
} else {
// a web service's method is called
HttpSession requestSession = servletRequest.getSession(false);
if (requestSession != null) { // the session is valid
chain.doFilter(request, response);
} else {
servletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
}
} else {
chain.doFilter(request, response);
}
}
/**
* Wittingly left empty
*/
public void destroy() {}
}
It seems that it is not very secure, because if the request's pathInfo is not in the enum, this request is passed on (just in case of some unexpected system calls).
Could you, please, suggest what to do, how to increase the security level. I want to build a configurable system (that is why I have the enum. It is possible just to add a path there to secure the web service and it is not required to duplicate the security code in the each web service). How to increase
Maybe I do not understand but.
jsessionid has nothink to do with security. you simply just get it.
Next I am not sure if you want authentication or authorization. The code as provided will not provide you with security features.
I suppose you are interested in authentication anyway. Security logic can be provided with standard web container features. Just send in authentication data in the header of request and you are done. web container can be configured to secure only selected resources (urls)