Install version 8
Describes how to install and configure Optimizely Service API version 8 for Commerce Connect 15, including the JSON Web Token bearer authentication flow that replaces OpenID Connect.
Service API version 8 is compatible with Optimizely Commerce Connect 15 and ships in the EPiServer.ServiceApi.Commerce package. Version 8 replaces the OpenID Connect authentication used in versions 6 and 7 with a built-in JSON Web Token (JWT) bearer token flow. The Service API issues and validates the tokens, so no external identity provider is required.
For Service API 6 or 7, see Install versions 6 and 7. This page applies to Service API 8 and Commerce Connect 15 only.
What changed in version 8
The following table compares authentication between the versions.
| Area | Versions 6 and 7 | Version 8 |
|---|---|---|
| Authentication package | EPiServer.OpenIDConnect | Built into EPiServer.ServiceApi, so no separate package |
| Token type | OAuth 2 bearer token issued by OpenID Connect | JWT bearer token issued by the Service API |
| Token endpoint | /api/episerver/connect/token | /episerverapi/auth/login |
| Grant types | client_credentials in version 6, password in version 7 | Username and password login, with refresh tokens |
| Refresh tokens | Not supported | Supported, with optional rotation and revocation |
Prerequisites
Complete the following before you install Service API 8.
- Install Optimizely Commerce Connect 15.
- Install Optimizely updates through the NuGet Package Manager in Visual Studio.
Install Service API
- Open your solution in Visual Studio.
- Select Tools > NuGet Package Manager > Manage NuGet Packages for Solution.
- Click Settings to create a source that points to the Optimizely NuGet feed.
- Open the Browse tab and select the source you created.
- Install EPiServer.ServiceApi and EPiServer.ServiceApi.Commerce version 8 or later.
Version 8 does not require the EPiServer.OpenIDConnect package. The Service API provides JWT authentication.
Configure authentication
Add the authentication settings
Add a ServiceApi:Authentication section to appsettings.json.
{
"ServiceApi": {
"Authentication": {
"SigningKey": "A_STRONG_SECRET_SIGNING_KEY",
"Authority": "https://mysite.com",
"Audience": "epi_service_api",
"RequireHttpsMetadata": true
}
}
}The section supports the following settings.
SigningKey– The symmetric secret that signs and validates JWT access tokens with HMAC-SHA256. Use a long, random value and store it securely.Authority– The token issuer. Set it to the site URL that issues tokens.Audience– The audience, or scope, that tokens are issued for. Defaults to the Service API scope,epi_service_api, when omitted.RequireHttpsMetadata– Whether HTTPS is required for authentication metadata. Set it totruefor production.
Keep the SigningKey out of source control. Store it in user secrets, environment variables, or a secret store, and rotate it if it is exposed.
Register the Service API
Register the Service API and its authentication in Startup.cs or Program.cs. The following extension method registers the credential validator, token service, refresh token store, and JWT bearer authentication.
public static IServiceCollection AddCommerceServiceApi(
this IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<PermissionService, AllowAllPermissionService>();
services.AddSingleton<ServiceApiTokenService>();
services.TryAddSingleton<IRefreshTokenStore, InMemoryRefreshTokenStore>();
services.AddScoped<IServiceApiCredentialValidator, DefaultServiceApiCredentialValidator<SiteUser>>();
var serviceApiAuth = configuration.GetSection("ServiceApi:Authentication");
services.AddServiceApiAuthentication(jwt =>
{
jwt.SigningKey = serviceApiAuth["SigningKey"];
jwt.Authority = serviceApiAuth["Authority"];
jwt.Audience = serviceApiAuth["Audience"] ?? ServiceApiOptionsDefaults.Scope;
jwt.RequireHttpsMetadata = bool.TryParse(serviceApiAuth["RequireHttpsMetadata"], out var https) && https;
});
return services;
}Call the extension method during service registration.
services.AddCommerceServiceApi(configuration);AllowAllPermissionService grants all permissions and is intended for evaluation only. In production, use a permission service that enforces the read and write access defined in Permissions for functions.
Customize credential validation and token storage
The default implementation validates credentials against ASP.NET Identity through UserManager and stores refresh tokens in memory. Replace either implementation by registering a custom type before you call AddCommerceServiceApi.
| Interface | Default implementation | Purpose |
|---|---|---|
IServiceApiCredentialValidator | DefaultServiceApiCredentialValidator<SiteUser> | Validates a username and password and builds the claims principal used to issue tokens. Supports account lockout. |
IRefreshTokenStore | InMemoryRefreshTokenStore | Stores, retrieves, revokes, and expires refresh tokens. |
Note
InMemoryRefreshTokenStoredoes not persist tokens across restarts or share them across instances. For load-balanced or production environments, implementIRefreshTokenStoreagainst a shared, durable store, such as a database.
Configure token options
ServiceApiAuthOptions controls access and refresh token behavior.
AccessTokenLifetime– How long an access token is valid. Returned to clients asexpires_in, in seconds.RefreshTokenLifetime– How long a refresh token is valid.RotateRefreshTokens– Whentrue, each refresh issues a new refresh token and revokes the previous one. Whenfalse, the same refresh token works until it expires.
Authentication endpoints
Service API 8 exposes JWT authentication endpoints under episerverapi/auth. All requests and responses use JSON.
Log in
Exchange a username and password for an access token and a refresh token.
POST /episerverapi/auth/login HTTP/1.1
Host: mysite.com
Content-Type: application/json
{
"username": "admin",
"password": "your-password"
}A successful request returns 200 OK.
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "3Qh8s0Zr...",
"token_type": "Bearer",
"expires_in": 3600
}The endpoint returns the following status codes.
200 OK– Returns the access and refresh tokens.401 Unauthorized– The username or password is incorrect (invalid_credentials).423 Locked– The account is locked because of multiple failed login attempts (account_locked).
Error responses use the following shape.
{
"error": "invalid_credentials",
"error_description": "The username or password is incorrect."
}The following cURL command logs in and returns a token pair.
curl --location --request POST 'https://mysite.com/episerverapi/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{ "username": "admin", "password": "your-password" }'Refresh a token
Exchange a valid refresh token for a new access token. When RotateRefreshTokens is enabled, the response also returns a new refresh token and revokes the previous one.
POST /episerverapi/auth/refresh HTTP/1.1
Host: mysite.com
Content-Type: application/json
{
"refresh_token": "3Qh8s0Zr..."
}The endpoint returns the following status codes.
200 OK– Returns a new access token and refresh token.401 Unauthorized– The refresh token is invalid, expired, or revoked (invalid_token).
Log out
Revoke a refresh token. This endpoint requires a valid access token.
POST /episerverapi/auth/logout HTTP/1.1
Host: mysite.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"refresh_token": "3Qh8s0Zr..."
}The endpoint returns the following status codes.
204 No Content– The refresh token is revoked.401 Unauthorized– The request is not authenticated.
Send a request with the token
Include the access token as a bearer token in the Authorization header of every Service API call. The following example imports a catalog.
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://mysite.com/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var content = new MultipartFormDataContent();
var filestream = new FileStream(path, FileMode.Open);
content.Add(new StreamContent(filestream), "file", "Catalog.zip");
var response = client.PostAsync("/episerverapi/commerce/import/catalog", content).Result;
if (response.StatusCode == HttpStatusCode.OK)
{
var returnString = response.Content.ReadAsStringAsync().Result;
returnString = returnString.Replace("\"", "");
Guid taskId = Guid.Empty;
Guid.TryParse(returnString, out taskId);
}
}Access token claims
The access token is a JWT signed with SigningKey using HMAC-SHA256. It includes the following claims, which the Service API uses for authorization.
jti– A unique token identifier.scope– The audience the token is issued for.subandnameidentifier– The authenticated user's ID.name– The user name.email– The user's email address, when available.role– One claim per role assigned to the user. Used to enforce read and write access.
Refresh token behavior
Refresh tokens extend a session without resending the user's credentials. The Service API handles them as follows.
- Refresh tokens are random values. The store keeps only a SHA-256 hash of each token, never the raw value.
- A refresh token is valid until it expires, based on
RefreshTokenLifetime, or until the logout endpoint or token rotation revokes it. - When
RotateRefreshTokensis enabled, each refresh issues a new refresh token and revokes the previous one, so each refresh token is valid for a single use. - When the user tied to a refresh token is no longer valid or is locked out, the Service API rejects and revokes the token.
Permissions for functions
The Service API enforces authorization for read and write operations through Permissions for Functions, based on the role claims in the access token.
- Go to Admin > Access Rights > Permissions for Functions.
- Select ReadAccess or WriteAccess under EPiServerServiceApi, depending on the permission you want to grant.
- Add the user or role that is present in the authenticated token's claims.
For example, to allow reading or updating the catalog, grant access to the /episerverapi/commerce/export/catalog/ and /episerverapi/commerce/import/catalog/ endpoints. Confirm the claim identity has the correct roles for read and write access to the Permissions for Functions for the Service API.
Strongly typed catalog content types
Strongly typed catalog content types must be present in the context of a Service API site. When you install the Service API to an existing website, the site resolves this automatically. When you install the Service API as a standalone application, deploy the assembly that contains the strongly typed catalog content types, and any dependencies of that assembly, to the Service API bin folder.
Troubleshoot Service API
Address the following issues when you set up the Service API.
- Confirm the server has a valid certificate from a trusted certificate authority for the site.
- Confirm all Service API requests use HTTPS.
- When tokens are rejected with a signature error, confirm the
SigningKeymatches between the issuing and validating configuration and has not changed since the token was issued. - When refresh tokens stop working after a restart or on a load-balanced site, replace the default in-memory refresh token store with a persistent
IRefreshTokenStoreimplementation.
The Service API has no rate limit.
See also Service API REST API reference.
Updated 7 days ago
