API authorization

Nesting Center API is protected and requires security tokens to access web services. User authorization is based on Microsoft identity platform. The platform provides a functionality to sign in users and grant access tokens, which can then be used to submit requests to Nesting Center API. A token is valid only for short period of time and allows client application to securely call the API.

User account

To start using Nesting Center API, you need to create an user account. If you haven’t created your account yet, please head over to the subsciptions page and create your free account.

HTTP requests

To call Nesting Center API from a client application, the application must request an access token and pass the retrieved token as a Bearer data in the Authorization header of the HTTP request.

Authorization header

// Include token in the Authorization header.
var request = new HttpRequestMessage()
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "Your token");
 
# Include token in the Authorization header.
headers = {'Authorization': 'Bearer ' + 'Your token'}
 
// Include token in the Authorization header.
HttpRequest header = HttpRequest.newBuilder()
   .POST(HttpRequest.BodyPublishers.ofString(data))
   .uri(URI.create(url))
   .header("Content-Type", "application/json")
   .header("Authorization", "Bearer " + "Your token")
   .build();

Getting access token

The authorization platform gives many ways to get access tokens, which can be acquired from a various application types and platforms. In following example we demonstrate how to use the interactive token acquisition, where authentication is delegated to the system browser. In the browser you enter user email address and password, sign in to the service and receive an access token for the application. Because authorization is integrated with the browser your application doesn’t need to access or store user login credentials. Another way, although it’s not recommended for a security reasons, is using authorization flow with a username and a password handled directly in public client applications.

Access token

// Required packages: Microsoft.Identity.Client

using Microsoft.Identity.Client;
using System.Linq;
using System.Threading.Tasks;

static class NestingCredentials
{
   private static readonly string serviceId = "14bc1c96-d677-4a08-8a07-68725b6bd732";
   private static readonly string redirectUri = "http://localhost";
   private static readonly string authority = "https://starsoftonline.b2clogin.com/tfp/starsoftonline.onmicrosoft.com/B2C_1_Sign";
   private static readonly string[] ApiScopes = {
      "https://starsoftonline.onmicrosoft.com/81588f52-db64-40bd-8096-e75159abdd9a/NestingCenter"
   };
   private static readonly IPublicClientApplication clientApplication;

   static NestingCredentials()
   {
      NestingCredentials.clientApplication = 
         PublicClientApplicationBuilder.Create(serviceId)
         .WithB2CAuthority(authority)
         .WithRedirectUri(redirectUri)
         .Build();

      // Optional: enable token cache serialization.
      // TokenCacheHelper.EnableSerialization(app.UserTokenCache);
   }

   public static async Task<string> AcquireToken()
   {
      try
      {
         var app = NestingCredentials.clientApplication;
         var accounts = await app.GetAccountsAsync();
         var firstAccount = accounts.FirstOrDefault();

         var authResult = await app.AcquireTokenSilent(NestingCredentials.ApiScopes, firstAccount).ExecuteAsync();

         return authResult.AccessToken;
      }
      catch (MsalUiRequiredException)
      {
            var authResult = await AcquireTokenInteractive();
            return authResult.AccessToken;
      }
   }

   private static async Task<AuthenticationResult> AcquireTokenInteractive()
   {
      return await NestingCredentials.clientApplication
         .AcquireTokenInteractive(NestingCredentials.ApiScopes)
         .WithUseEmbeddedWebView(false)
         .ExecuteAsync();
   }

   private static async Task<AuthenticationResult> AcquireTokenWithUsernameAndPassword()
   {
      // Note: requires authority key B2C_1_ROPC instead of B2C_1_Sign.
      string username = "";
      string password = "";
      var securePassword = new System.Security.SecureString();

      foreach (char c in password)
      {
         securePassword.AppendChar(c);
      }

      return await NestingCredentials.clientApplication
         .AcquireTokenByUsernamePassword(NestingCredentials.ApiScopes, username, securePassword)
         .ExecuteAsync();
   }
}
// Required packages: msal
from msal import PublicClientApplication

class NestingCredentials:
   """NestingCenter API."""

   __serviceId = "14bc1c96-d677-4a08-8a07-68725b6bd732"
   __authority = "https://starsoftonline.b2clogin.com/tfp/starsoftonline.onmicrosoft.com/B2C_1_Sign"
   __apiScopes = ["https://starsoftonline.onmicrosoft.com/81588f52-db64-40bd-8096-e75159abdd9a/NestingCenter"]
   __client_application = PublicClientApplication(
        client_id =__serviceId,
        authority =__authority)

   @staticmethod
   def acquire_token():
       token = NestingCredentials.__acquire_token_silent()
       if token is None:
           token = NestingCredentials.__acquire_token_interactive()
       return token

   @staticmethod
   def __acquire_token_silent():
      accounts = NestingCredentials.__client_application.get_accounts()
      if accounts:
         auth_result = NestingCredentials.__client_application.acquire_token_silent(
                          NestingCredentials.__apiScopes, accounts[0])
         try:
            return auth_result["access_token"]
         except KeyError:
            return None
      return None

   @staticmethod
   def __acquire_token_interactive():
      auth_result = NestingCredentials.__client_application.acquire_token_interactive(NestingCredentials.__apiScopes)

      try:
         return auth_result["access_token"]
      except KeyError:
         print("Error: " + auth_result.get("error"))
         print("Error description: " + auth_result.get("error_description"))
      return None
// Required packages: com.microsoft.azure:msal4j
import com.microsoft.aad.msal4j.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletionException;

public class NestingCredentials {

   private static final String serviceId = "14bc1c96-d677-4a08-8a07-68725b6bd732";
   private static final String authority = "https://starsoftonline.b2clogin.com/tfp/starsoftonline.onmicrosoft.com/B2C_1_Sign";
   private static final String redirectUri = "http://localhost";
   private static final String[] apiScopes = {"https://starsoftonline.onmicrosoft.com/81588f52-db64-40bd-8096-e75159abdd9a/NestingCenter"};

   private static final PublicClientApplication clientApplication = buildApplication();

   private static PublicClientApplication buildApplication() {
      try {
         return PublicClientApplication.builder(serviceId)
                .b2cAuthority(authority)
                .build();
      } catch (MalformedURLException e) {
          e.printStackTrace();
          return null;
      }
   }

   public static String acquireToken() {
      String token = NestingCredentials.acquireTokenSilent();
      if (token == null || token.isEmpty()) {
         token = NestingCredentials.acquireTokenInteractive();
      }

      return token;
   }

   private static String acquireTokenSilent() {
      SilentParameters parameters = SilentParameters
             .builder(Set.of(NestingCredentials.apiScopes))
             .build();

      try {
         IAuthenticationResult result = NestingCredentials.clientApplication
                 .acquireTokenSilently(parameters)
                 .join();

         return result.accessToken();
      } catch (MalformedURLException | CompletionException | CancellationException e) {
         return null;
      }
   }

   private static String acquireTokenInteractive() {
      try {

         InteractiveRequestParameters parameters = InteractiveRequestParameters
                 .builder(new URI(redirectUri))
                 .scopes(Set.of(NestingCredentials.apiScopes))
                 .build();

          IAuthenticationResult result = NestingCredentials.clientApplication
                 .acquireToken(parameters)
                 .join();

          return result.accessToken();
      } catch (URISyntaxException | CompletionException e) {
          e.printStackTrace();
          return null;
      }
   }
}

Optional token cache serialization

Windows applications store the token in memory and user information lasts only for the duration of the application. When login in interactively, the browser window is opened every time the application starts and user is asked for credentials. To reduce number of times the browser is opened, you can use optional token cache serialization interface and store token cache between application sessions. First time the application starts user enters credentials in the browser and later the cache is used without interaction. Below is an example of a naive implementation of custom serialization of a token cache for desktop applications. You can also refer to Microsoft.Identity.Client.Extensions.Msal package which provides secure and configurable token cache persistence implementation. For more details please see Cross platform Token Cache for dotnet

Serialization

// Enable the serialization in NestingCredentials constructor.
TokenCacheHelper.EnableSerialization(app.UserTokenCache);

// Token cache helper class.
using Microsoft.Identity.Client;
using System;
using System.IO;
using System.Security.Cryptography;

static class TokenCacheHelper
{
   // Path to the token cache.
   public static readonly string CacheFilePath = 
      $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\NestingCenter\\msalcache.bin";

   private static readonly object FileLock = new object();

   public static void  EnableSerialization(ITokenCache tokenCache)
   {
      tokenCache.SetBeforeAccess(BeforeAccessNotification);
      tokenCache.SetAfterAccess(AfterAccessNotification);
   }

   private static void BeforeAccessNotification(TokenCacheNotificationArgs args)
   {
      lock (FileLock)
      {
         args.TokenCache.DeserializeMsalV3(File.Exists(CacheFilePath)
            ? ProtectedData.Unprotect(File.ReadAllBytes(CacheFilePath),
               null, DataProtectionScope.CurrentUser)
            : null);
      }
   }

   private static void AfterAccessNotification(TokenCacheNotificationArgs args)
   {
      // Operation resulted in a cache update.
      if (args.HasStateChanged)
      {
         lock (FileLock)
         {
            try
            {
               File.WriteAllBytes(CacheFilePath, ProtectedData.Protect(
                  args.TokenCache.SerializeMsalV3(), null, DataProtectionScope.CurrentUser));
            }
            catch (DirectoryNotFoundException)
            {
               Directory.CreateDirectory(Path.GetDirectoryName(CacheFilePath));
            }
         }
      }
   }
}
# Required packages: msal_extensions
from os import path
from msal import PublicClientApplication
from msal_extensions import FilePersistenceWithDataProtection, PersistedTokenCache

# Enable token cache.
__client_application = PublicClientApplication(
   client_id=__serviceId,
   authority=__authority,
   token_cache=PersistedTokenCache(
       FilePersistenceWithDataProtection(
           path.expandvars(r'%LOCALAPPDATA%\NestingCenter\msal_cache.bin'))))
import com.microsoft.aad.msal4j.*;
import com.microsoft.aad.msal4jextensions.PersistenceSettings;
import com.microsoft.aad.msal4jextensions.PersistenceTokenCacheAccessAspect;

private static PersistenceSettings createPersistenceSettings() throws IOException {

   Path path = Paths.get(System.getenv("LOCALAPPDATA"), "NestingCenter");
   return PersistenceSettings.builder("msal_cache.bin", path)
          .build();
}

private static ITokenCacheAccessAspect createPersistenceAspect() throws IOException {
   return new PersistenceTokenCacheAccessAspect(createPersistenceSettings());
}

// Enable token cache in buildApplication method.
return PublicClientApplication.builder(serviceId)
         .b2cAuthority(authority)
         .setTokenCacheAccessAspect(createPersistenceAspect())
         .build();