Developer forum

Forum » Dynamicweb 10 » Extend Delivery API

Extend Delivery API

Adrian Ursu Dynamicweb Employee
Adrian Ursu
Reply

Hi guys,

Is it possible to extend the Delivery API with additional endpoints?
The official documentation seems to miss some information: https://doc.dynamicweb.dev/documentation/extending/extensibilitypoints/services-middleware.html

Thank you,

Adrian


Replies

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi Adrian,

It depends on what you're trying to achieve.

You can add all the endpoints you want to the solution. As a rule of thumb, you shouldn't use the prefixes /admin or /dwapi for your routes but otherwise you're free to do what you want, be that controllers or middleware, or anything really. It is possible to add new endpoints to the /dwapi route prefix, but it's considered experimental and the APIs may change. If you want to add something to the scope of an existing controller in the Delivery API, that's not possible.

If you can clarify what you want to do, I can better help you further.

- Jeppe

 
Adrian Ursu Dynamicweb Employee
Adrian Ursu
Reply

Hi Jeppe,

Thank you for the response.

My use case is related to an ERP integration. The approach is to change how the communication goes. We have to expose some endpoints for receiving Customer information (Users, Addresses) both with Create and Update and also for receiving Products (Products, Product groups, ProductAttributes, Product Assignment).
At first glance, the current Delivery API supports some if not all of these scenarios.
The flow of data will be minimal as the customer does not have a lot of changes to Customers and Products but I am concerned if something is not supported already by the current Delivery API and we'll have to add new endpoints.
I would try to use a single Authentication and API definition for the entire integration. Hence the question about extending the current Delivery API instead of creating a new one from scratch.

Does it make sense?

Thank you,

Adrian

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Hi Adrian

Extending the delivery api  can be 2 things:

  1. Adding new properties to existing response viewmodels - e.g. a new property on a product
  2. Adding new endpoints that receives or send other data than what dwapi does.

My guess is you are looking for option 2.

That would simply mean registering your own endpoints on your own route (url - e.g. /customapi/. That is very simple to do: https://doc.dynamicweb.dev/documentation/extending/middleware/index.html#implementation-example---custom-web-api

If you want to use DW permissions and login using /dwapi authtentication and JWT tokens, you can add the 

[PermissionFilter] (Dynamicweb.Frontend.Classic.Api namespace) attribute to your endpoints.

    [PermissionFilter]
    public IActionResult Some()
    {
        int userId = this.GetUserId();
        User? currentUser = null;
        if (userId > 0)
        {
            currentUser = UserManagementServices.Users.GetUserById(userId);
        }

}

BR Nicolai

 
Adrian Ursu Dynamicweb Employee
Adrian Ursu
Reply

Hi Nicolai,

I believe both options are useful.
Do you see any potential issues if we use the delivery APi for the purpose described?

Thank you very much.

Adrian

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

No - as long as the delivery api exposes the data you need, all is fine.

If you need personalized data (custom prices or permissions) you will need to handle auth and jwt token storage on the consuming sides of things. That is the only thing to be aware of.

 
Jon Thorne
Jon Thorne
Reply

Hi Nicolai,

Can you explain a bit more about how to use the PermissionFilter for my own custom API?

I would like to use a custom api while logged into the swift frontend. But I can't seem to get the "/dwapi/auth/token" endpoint ot work as described in:

https://doc.dynamicweb.dev/documentation/headless/delivery-api/access.html

Regards,

Jon.

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Hi Jon

The example on the doc site has the wrong URL - and describes overrriding expiration wrong. Below is a better script.

// Accept default (1800s):
const response = await fetch('/dwapi/users/token', {
  method: 'POST',
  credentials: 'include'
});

// Custom expiration:
const response = await fetch('/dwapi/users/token?expirationInSeconds=3600', {
  method: 'POST',
  credentials: 'include'
});

 

Register your custom API assembly and use permissionfilter

Your assembly with your custom api endpoint needs a class implementing both `IPipeline` and `IWebApi`. The `WebApiPipeline` automatically discovers all `IWebApi` assemblies at startup and adds them as MVC application parts --- so just implementing the interface is enough:

```
using Dynamicweb.Extensibility.AddIns;
using Dynamicweb.Host.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

[AddIn]
public class MyApiPipeline : IPipeline, IWebApi
{
    public int Rank => 200; // must be > 100 to run after built-in DW pipelines

    public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder) { }
    public void RegisterApplicationComponents(IApplicationBuilder app) { }
    public void RunInitializers() { }
}

```

Create your controller

Reference `Dynamicweb.Frontend.Classic.Api` and apply `[PermissionFilter]` to endpoints that need an authenticated user:

```
using Dynamicweb.Frontend.Classic.Api;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("dwapi/myapi")]
public class MyApiController : ControllerBase
{
    [HttpGet("data")]
    [PermissionFilter]  // requires valid JWT in Authorization: Bearer header
    public IActionResult GetData()
    {
        // HttpContext.User is populated by UserTokenFilter (base of PermissionFilter)
        // DW PermissionContext is also set, so DW permission checks work normally
        return Ok(new { message = "hello" });
    }
}

```

What `PermissionFilter` actually does

The chain is:

```
PermissionFilter
  └─ UserTokenFilter (base)
       └─ reads Authorization: Bearer <token> header
          └─ validates JWT via JwtService.GetPrincipal()
             └─ sets HttpContext.User from claims
  └─ resolves DW User from claims
     └─ sets PermissionContext.FrontendUserContext(user) on HttpContext.Items
        └─ so DW's built-in permission checks work within the request scope

```

 
Jon Thorne
Jon Thorne
Reply

Hi Nicolai,

I have the custom API working no problem. I am having an issue trying to use the cookie based auth that is already in use in swift to call the custom API without having to login again. The comments on this example suggest that it is possible.

The main issue with the documentation is that the endpoint "/dwapi/users/token" just gives me 404 not found error. Has this been implemented in a newer version of DW? Currently I am testing with v10.23.6.

Regards,

Jon.

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply
This post has been marked as an answer

Hi Jon

/dwapi/users/token is very new. So you have to have a version supporting it... You can see it in /dwapi/docs/index.html?url=/dwapi/api.json if it is available on your installation.

That specific endpoint will echange your cookie with a jwt that can be used on endpoints decorated with the permission filter.

Alternatively you can decorate your endpoint with a Authorize attribute like below to support cookie based authentication.

using Dynamicweb.Frontend.Classic.Authentication
[Authorize(AuthenticationSchemes = AuthenticationSchemeNames.ExtranetCookie)]

And then get the user like this:
var user = AuthenticationHelper.GetUser(User);

I would generally recommend using JWT

Votes for this answer: 1
 
Jon Thorne
Jon Thorne
Reply

Thanks Nicolai. That is very helpful info and worked for what I needed.

I have a few extra comments that may be helpful for others who may want to try this.

AuthenticationHelper and AuthenticationSchemeNames are protected classes, so I needed to implement this in custom code.

[Authorize(AuthenticationSchemes = "Dynamicweb.Extranet")]
int userId = Convert.ToInt32(User.FindFirstValue(UserClaimTypes.UserId));
User? user = UserManagementServices.Users.GetUserById(userId);

And also a few includes that are needed:

using Dynamicweb.Host.Core.Authentication;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

 

 

 

You must be logged in to post in the forum