Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

OrderCloud User Authentication

When a user authenticates and acquires an access token from OrderCloud, typically in a front-end web or mobile app, that token can be used in your custom endpoints to verify the user's identity and roles. Here are the steps involved:

1. Register OrderCloud user authentication and register user context in your Startup class.

// In Startup.cs
public virtual void ConfigureServices(IServiceCollection services) {
    services.AddOrderCloudUserAuth(opts => opts.AddValidClientIDs("client_id_1", "client_id_2"));
}

2. In your front-end app, anywhere you call one of your custom endpoints, pass the OrderCloud.io access token in a request header.

Authorization: Bearer my-ordercloud-token

3. Mark any of your controllers or action methods with [OrderCloudUserAuth]. Use the CatalystBaseController property UserContext.

Optionally, You may provide one or more required roles in this attribute, any one of which the user must be assigned in order for authorization to succeed.

public class MyThingController : CatalystController
{
    // Without access, requestor recieves a 401 Unauthorized or 403 InsufficientRoles error.
    [HttpGet, Route("thing")] 
    [OrderCloudUserAuth(ApiRole.Shopper, ApiRole.OrderReader, ApiRole.OrderAdmin)] // Any one of these threee roles gives access the endpoint 
    public Thing Get(string id) {
        var username = UserContext.Username; // UserContext is a property on CatalystController
        ...
    }
}

Define custom roles that are meaningful in your app's context. Give users access to these roles through https://ordercloud.io/api-reference/authentication-and-authorization/security-profiles/create

    [HttpPut, Route("thing")]
    [OrderCloudUserAuth("ThingAdmin")] // The role "ThingAdmin" is a custom developer-defined role
    public void Edit([FromBody] Thing thing) {
        ...
    }

Access data in the claims of the OrderCloud token used in the request.

    [HttpPut, Route("hello")]
    [OrderCloudUserAuth] // No roles are defined, so any valid OrderCloud Token gives access.
    public string Hello([FromBody] Thing thing) {
        return $"Hello {UserContext.Username}, your role is {UserContext.CommerceRole}";.
    }

Restrict route access so it can only be used by specific user types (Buyer, Supplier, Seller). Only works with OrderCloudUserAuth.

    [HttpPut, Route("hello")]
    [OrderCloudUserAuth, UserTypeRestrictedTo(CommerceRole.Supplier)] // Any supplier user token can access
    public string Hello([FromBody] Thing thing) {
        return $"Hello Supplier User";.
    }

Get the full user details such as FirstName, LastName and xp from the GET /me endpoint

    [HttpPut, Route("hello")]
    [OrderCloudUserAuth] // No roles are defined, so any valid OrderCloud Token gives access.
    public async Task<string> Hello([FromBody] Thing thing) {
        var user = await _oc.Me.GetAsync(UserContext.AccessToken)

        return $"Hello {user.FirstName} {user.LastName}"; // now no error thrown
    }

Proxy the OrderCloud API, adding your own permission logic

    [HttpGet, Route("orders/mystore")]
    [OrderCloudUserAuth(ApiRole.Shopper)] 
    public async Task<ListPage<Order>> ListOrdersFromMyStore(ListArgs args) {
        // A different way to get the user details on MeUser. Make any request from OcClient as the authenticated user.
        var me = await await UserContext.BuildClient().Me.GetAsync();
        // Access a developer-defined extended property (xp) on the user called "StoreID".
        var storeID = me.xp.StoreID 
        // Create a filter. Only return orders where Order.BillingAddress.ID equals the user's storeID.   
        var billingAddressFilter = new ListFilter("BillingAddress.ID", storeID);
        // Add the filter on top of any additional api user-defined filters. 
        args.Filters.Add(billingAddressFilter);
        // request orders from an admin perspective
        return new OrderCloudClient(...).Orders.ListAsync(OrderDirection.Outgoing, page: args.Page, pageSize: args.PageSize, filters: args.ToFilterString()) 
    }
}

DecodedToken and IRequestAuthenticationService

The primary way to enforce authentication in controllers is by using the [OrderCloudUserAuth] attribute on your controller actions. This automatically validates tokens and injects user context for you.

However, if you prefer to handle authentication inside a service (rather than directly in the controller), IRequestAuthenticationService offers an alternative approach. It allows you to verify tokens and retrieve user details programmatically.

Important: IRequestAuthenticationService depends on HttpContext and should only be used within the ASP.NET Core pipeline (e.g., controllers, middleware, or services called from them). For environments without HttpContext (such as Azure Functions, background services, or console apps), use ITokenValidator instead. ITokenValidator works independently of the current request

Using IRequestAuthenticationService (ASP.NET Core)

    string rawToken = "...";
    // Parses the token, but does not verify it. 
    DecodedToken context = new DecodedToken(rawToken);
    // Only data on the token is available. user.FirstName and user.xp are not, for example.
    console.log(user.Username)

    // Inject a IRequestAuthenticationService to verify. [OrderCloudUserAuth] uses this method under the hood.
    var options = new OrderCloudUserAuthOptions
    {
        ValidClientIDs = new[] { "YOUR_CLIENT_ID" }
    };
    DecodedToken verified = await _requestAuthenticationService.VerifyTokenAsync(rawToken, options); 

    // IRequestAuthenticationService can also get a DecodedToken from the current HttpContext.
    // Only use this after calling VerifyTokenAsync, either directly or through [OrderCloudUserAuth].
    DecodedToken unverified = await _requestAuthenticationService.GetDecodedToken(); 

    // A shortcut method for getting the full user details
    MeUser user = await _requestAuthenticationService.GetUserAsync(); 

Inject the IRequestAuthenticationService into a command class. Within a method, an OrderCloud request can be made using that user's token.

public class OrderSubmitCommand 
{
    private readonly IOrderCloudClient _oc;      // Injected with Integration Client ID context. FullAccess "super user".
    private readonly IRequestAuthenticationService _auth; // User token that made the request 
    private readonly ICreditCardCommand _card;   // Details of card processing left unopinionated
    
    public OrderSubmitCommand(IOrderCloudClient oc, IRequestAuthenticationService auth, ICreditCardCommand card)
    {
        _oc = oc;
        _auth = auth;
        _card = card;
    }

    public async Task<Order> SubmitOrderAsync(string orderID, OrderDirection direction, OrderCloudIntegrationsCreditCardPayment payment) 
    {
        // Order details, inlcuding all line items. Requested with "super user" client context.
        var worksheet = await _oc.IntegrationEvents.GetWorksheetAsync(OrderDirection.Incoming, orderID);
        // Perform validation with full "super user" data
        await ValidateOrderAsync(worksheet, payment);
        await _card.AuthorizePayment(payment);  // do this in middleware for security.
        try
        {
            // Make the submit order request with the original user's token.
            return await _auth.GetClient().Orders.SubmitAsync(direction, orderID); 
        }
        catch (Exception)
        {
            await _card.VoidPaymentAsync(orderID);
            throw;
        }
    }
}

Using ITokenValidator (Non-ASP.NET Contexts like Azure Functions)

ITokenValidator does not depend on HttpContext and works anywhere you have the raw token:

public class TokenValidationExample
{
    private readonly ITokenValidator _tokenValidator;

    public TokenValidationExample(ITokenValidator tokenValidator)
    {
        _tokenValidator = tokenValidator;
    }

    public async Task ValidateTokenAsync(string rawToken)
    {
        var options = new OrderCloudUserAuthOptions
        {
            ValidClientIDs = new[] { "YOUR_CLIENT_ID" }
        };

        // Validate token and enforce roles/user types if needed
        DecodedToken decoded = await _tokenValidator.ValidateAccessTokenAsync(
            rawToken,
            options
        );

        Console.WriteLine($"Token is valid for user: {decoded.Username}");
    }
}