Skip to content

Commit a27ce06

Browse files
authored
Create production-setup-oauth.md
1 parent edba11d commit a27ce06

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
---
2+
description: Setup OAuth authorization for Umbraco Management API in local and production environments.
3+
---
4+
5+
# Overview
6+
7+
{% hint style="info" %}
8+
9+
This guide is created by a community member and is not managed by Umbraco HQ. Some attributes or features may evolve as the Management API continues to develop.
10+
11+
{% endhint %}
12+
13+
This guide covers how to set up OAuth authorization for the Umbraco Management API for both local development and production environments. Authorization configuration can differ greatly between environments, and understanding these differences is key.
14+
15+
Before proceeding, it is recommended to read the [Management API overview](./README.md). It provides fundamental information about the authorization process and its significance.
16+
17+
This guide will walk through:
18+
19+
1. [Environment Differences and Challenges](#environment-differences-and-challenges)
20+
2. [Configuring appsettings.json](#configuring-appsettingsjson)
21+
3. [Setting up Production-Local Authorization](#setting-up-production-local-authorization)
22+
4. [Creating a Custom Client ID](#creating-a-custom-client-id)
23+
5. [Minimal API Implementation](#minimal-api-implementation)
24+
6. [Configuring Authorization in Production](#configuring-authorization-in-production)
25+
7. [Common Pitfalls and Troubleshooting](#common-pitfalls-and-troubleshooting)
26+
27+
# Environment Differences and Challenges
28+
29+
The Umbraco Management API authorization works seamlessly in non-production environments using tools like Swagger or Postman. However, in production, some key differences and limitations exist:
30+
31+
- **Swagger and Postman Integration**: Only allowed in non-production, facilitating testing.
32+
- **Client Restrictions**: In production, only the `umbraco-back-office` client is allowed.
33+
- **OAuth2 Flows:** Requires careful handling to ensure secure setup without Swagger available.
34+
35+
To avoid conflicts and guarantee smooth integration in production, it's crucial to create a custom client and tailor the authorization flow accordingly.
36+
37+
# Configuring appsettings.json
38+
39+
To override the default callback URL for OAuth authorization, update the `appsettings.json` file as follows: (this uses client: `umbraco-back-office`)
40+
41+
42+
```json
43+
"Umbraco": {
44+
"CMS": {
45+
"Security": {
46+
"AuthorizeCallbackPathName": "/callback"
47+
}
48+
}
49+
}
50+
```
51+
52+
This configuration specifies a custom callback path for OAuth. However, it may interfere with the default backoffice callback, affecting accessibility.
53+
54+
# Setting up Production-Local Authorization
55+
56+
In a production environment, Swagger UI is disabled, and only the `umbraco-back-office` client can be used. This requires a more advanced approach.
57+
58+
# Creating a Custom Client ID
59+
60+
To avoid conflicts with the backoffice, a new client should be created. Below are the steps to set up a custom client using a Minimal API:
61+
62+
## Extending `OpenIdDictApplicationManagerBase`
63+
64+
Create a new client for production use by extending the `OpenIdDictApplicationManagerBase`.
65+
66+
```csharp
67+
public class CustomApplicationManager : OpenIdDictApplicationManagerBase
68+
{
69+
public CustomApplicationManager(IOpenIddictApplicationManager applicationManager)
70+
: base(applicationManager)
71+
{
72+
}
73+
74+
public async Task EnsureCustomApplicationAsync(string clientId, Uri redirectUri, CancellationToken cancellationToken = default)
75+
{
76+
if (!redirectUri.IsAbsoluteUri)
77+
{
78+
throw new ArgumentException("The provided URL must be an absolute URL.", nameof(redirectUri));
79+
}
80+
81+
var clientDescriptor = new OpenIddictApplicationDescriptor
82+
{
83+
DisplayName = "Custom Application",
84+
ClientId = clientId,
85+
RedirectUris = { redirectUri },
86+
ClientType = OpenIddictConstants.ClientTypes.Public,
87+
Permissions = {
88+
OpenIddictConstants.Permissions.Endpoints.Authorization,
89+
OpenIddictConstants.Permissions.Endpoints.Token,
90+
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
91+
OpenIddictConstants.Permissions.ResponseTypes.Code
92+
}
93+
};
94+
95+
await CreateOrUpdate(clientDescriptor, cancellationToken);
96+
}
97+
}
98+
```
99+
100+
The above code allows you to define a new custom client. This client will not interfere with the existing `umbraco-back-office` client, ensuring smooth integration and avoiding callback conflicts.
101+
102+
# Minimal API Implementation
103+
104+
To set up a Minimal API that integrates the custom client, follow these steps:
105+
106+
## Creating the Minimal API Application
107+
108+
Below is a complete setup for using Minimal API to create and manage custom OAuth clients for the Umbraco Management API.
109+
110+
```csharp
111+
builder.Services.AddScoped<CustomApplicationManager>(provider =>
112+
{
113+
var applicationManager = provider.GetRequiredService<IOpenIddictApplicationManager>();
114+
return new CustomApplicationManager(applicationManager);
115+
});
116+
117+
app.MapPost("/create-client", async (ClientModel model, CustomApplicationManager applicationManager) =>
118+
{
119+
try
120+
{
121+
if (string.IsNullOrEmpty(model.ClientId))
122+
return Results.BadRequest("Client ID is required.");
123+
124+
if (!Uri.TryCreate(model.RedirectUri, UriKind.Absolute, out var redirectUri))
125+
return Results.BadRequest("Invalid redirect URI.");
126+
127+
await applicationManager.EnsureCustomApplicationAsync(model.ClientId, redirectUri);
128+
return Results.Ok("Client created/updated successfully.");
129+
}
130+
catch (Exception ex)
131+
{
132+
return Results.Problem(ex.Message);
133+
}
134+
}).WithName("CreateClient");
135+
136+
app.MapGet("/login", (Auth auth, IConfiguration config, IBackOfficeApplicationManager backOfficeApplicationManager) =>
137+
{
138+
var baseUrl = config["Umbraco:BaseUrl"];
139+
var authorizationUrl = auth.GetAuthorizationUrl();
140+
return Results.Redirect(baseUrl + authorizationUrl);
141+
});
142+
143+
app.MapGet("/callback", async (Auth auth, HttpContext httpContext, IConfiguration configuration) =>
144+
{
145+
var code = httpContext.Request.Query["code"];
146+
var state = httpContext.Request.Query["state"];
147+
if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(state))
148+
{
149+
return Results.BadRequest("Invalid callback parameters");
150+
}
151+
try
152+
{
153+
var tokenResponse = await auth.HandleCallback(code, state);
154+
//
155+
return Results.Redirect("/dashboard");
156+
}
157+
catch (Exception ex)
158+
{
159+
return Results.BadRequest($"Authentication failed: {ex.Message}");
160+
}
161+
});
162+
163+
public class ClientModel
164+
{
165+
public string ClientId { get; set; }
166+
public string RedirectUri { get; set; }
167+
}
168+
```
169+
170+
This implementation demonstrates how to use Minimal API to manage OAuth clients dynamically, allowing better integration into production workflows.
171+
172+
# Configuring Authorization in Production
173+
174+
To configure authorization using the custom client:
175+
176+
1. Update your `appsettings.json` file:
177+
178+
```json
179+
{
180+
"BaseUrl": "https://your-production-domain",
181+
"ClientId": "newclientId", // Set to the new custom client ID you created
182+
"AuthorizationEndpoint": "/umbraco/management/api/v1/security/back-office/authorize",
183+
"TokenEndpoint": "/umbraco/management/api/v1/security/back-office/token",
184+
"RedirectUri": "https://your-production-domain/callback" // Callback URL for your newly created client ID
185+
}
186+
```
187+
188+
2. Use the custom client manager endpoint (`/create-client`) to create a new client for use in production.
189+
190+
3. Handle token retrieval and secure storage in your application. Store tokens securely to avoid exposure, for instance by using HTTP-only cookies.
191+
192+
# Common Pitfalls and Troubleshooting
193+
194+
## Callback Interference with Back Office
195+
196+
If the `umbraco-back-office` client causes callback conflicts, use a custom client with a distinct redirect URI to prevent overlap with backoffice authentication.

0 commit comments

Comments
 (0)