From d63bef9c99967f5f0ad00d03a25f834eb136c1d3 Mon Sep 17 00:00:00 2001 From: AleF83 Date: Thu, 5 Aug 2021 14:30:10 +0300 Subject: [PATCH 1/5] BasePath feature --- e2e/tests/base-path.spec.ts | 33 +++++++++++++++++++++++++++ src/Helpers/AspNetServicesHelper.cs | 4 +++- src/Middlewares/BasePathMiddleware.cs | 33 +++++++++++++++++++++++++++ src/Startup.cs | 14 +++++++++++- 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 e2e/tests/base-path.spec.ts create mode 100644 src/Middlewares/BasePathMiddleware.cs diff --git a/e2e/tests/base-path.spec.ts b/e2e/tests/base-path.spec.ts new file mode 100644 index 0000000..563ba1c --- /dev/null +++ b/e2e/tests/base-path.spec.ts @@ -0,0 +1,33 @@ +import * as querystring from 'querystring'; +import * as dotenv from 'dotenv'; +import axios from 'axios'; + +import clients from '../config/clients-configuration.json'; +import type { Client } from '../types'; + +describe('Base path', () => { + let client: Client; + + beforeAll(() => { + dotenv.config(); + client = clients.find(c => c.ClientId === 'client-credentials-flow-client-id'); + expect(client).toBeDefined(); + }); + + test('Discovery Endpoint', async () => { + const response = await axios.get(process.env.OIDC_DISCOVERY_ENDPOINT_WITH_BASE_PATH); + expect(response.data.token_endpoint).toEqual(process.env.OIDC_TOKEN_URL_WITH_BASE_PATH); + }); + + test('Token Endpoint', async () => { + const parameters = { + client_id: client.ClientId, + client_secret: client.ClientSecrets?.[0], + grant_type: 'client_credentials', + scope: client.AllowedScopes.join(' '), + }; + + const response = await axios.post(process.env.OIDC_TOKEN_URL_WITH_BASE_PATH, querystring.stringify(parameters)); + expect(response).toBeDefined(); + }); +}); diff --git a/src/Helpers/AspNetServicesHelper.cs b/src/Helpers/AspNetServicesHelper.cs index 85803cb..27ca8e2 100644 --- a/src/Helpers/AspNetServicesHelper.cs +++ b/src/Helpers/AspNetServicesHelper.cs @@ -15,6 +15,8 @@ public class AspNetServicesOptions public SessionOptions Session { get; set; } public ForwardedHeadersOptions ForwardedHeadersOptions { get; set; } + + public string BasePath { get; set; } } public class AuthenticationOptions @@ -82,4 +84,4 @@ private static void ConfigureCors(IServiceCollection services, AspNetCorsOptions ); } } -} \ No newline at end of file +} diff --git a/src/Middlewares/BasePathMiddleware.cs b/src/Middlewares/BasePathMiddleware.cs new file mode 100644 index 0000000..9ac9522 --- /dev/null +++ b/src/Middlewares/BasePathMiddleware.cs @@ -0,0 +1,33 @@ +using IdentityServer4.Extensions; +using Microsoft.AspNetCore.Http; +using System.Threading.Tasks; +using IdentityServer4.Configuration; + +#pragma warning disable 1591 + +namespace OpenIdConnectServer.Middlewares +{ + public class BasePathMiddleware + { + private readonly RequestDelegate _next; + private readonly IdentityServerOptions _options; + + public BasePathMiddleware(RequestDelegate next, IdentityServerOptions options) + { + _next = next; + _options = options; + } + + public async Task Invoke(HttpContext context) + { + var basePath = Config.GetAspNetServicesOptions().BasePath; + var request = context.Request; + if(request.Path.Value.Length > basePath.Length) + { + request.Path = request.Path.Value.Substring(basePath.Length); + context.SetIdentityServerBasePath(basePath); + } + await _next(context); + } + } +} diff --git a/src/Startup.cs b/src/Startup.cs index c52ba1f..e32089a 100644 --- a/src/Startup.cs +++ b/src/Startup.cs @@ -6,10 +6,12 @@ using OpenIdConnectServer.Validation; using OpenIdConnectServer.JsonConverters; using Newtonsoft.Json.Serialization; +using OpenIdConnectServer.Middlewares; +using IdentityServer4.Hosting; namespace OpenIdConnectServer { - public class Startup + public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 @@ -52,6 +54,16 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) AspNetServicesHelper.UseAspNetServices(app, aspNetServicesOptions); app.UseIdentityServer(); + + var basePath = Config.GetAspNetServicesOptions().BasePath; + if (!string.IsNullOrEmpty(basePath)) + { + app.UseWhen(ctx => ctx.Request.Path.StartsWithSegments(basePath), appBuilder => { + appBuilder.UseMiddleware(); + appBuilder.UseMiddleware(); + }); + } + app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); From b9f0531c25771182c0fa2f8b2015757d46274ada Mon Sep 17 00:00:00 2001 From: AleF83 Date: Thu, 5 Aug 2021 14:35:24 +0300 Subject: [PATCH 2/5] fixup! BasePath feature --- e2e/tests/base-path.spec.ts | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 e2e/tests/base-path.spec.ts diff --git a/e2e/tests/base-path.spec.ts b/e2e/tests/base-path.spec.ts deleted file mode 100644 index 563ba1c..0000000 --- a/e2e/tests/base-path.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as querystring from 'querystring'; -import * as dotenv from 'dotenv'; -import axios from 'axios'; - -import clients from '../config/clients-configuration.json'; -import type { Client } from '../types'; - -describe('Base path', () => { - let client: Client; - - beforeAll(() => { - dotenv.config(); - client = clients.find(c => c.ClientId === 'client-credentials-flow-client-id'); - expect(client).toBeDefined(); - }); - - test('Discovery Endpoint', async () => { - const response = await axios.get(process.env.OIDC_DISCOVERY_ENDPOINT_WITH_BASE_PATH); - expect(response.data.token_endpoint).toEqual(process.env.OIDC_TOKEN_URL_WITH_BASE_PATH); - }); - - test('Token Endpoint', async () => { - const parameters = { - client_id: client.ClientId, - client_secret: client.ClientSecrets?.[0], - grant_type: 'client_credentials', - scope: client.AllowedScopes.join(' '), - }; - - const response = await axios.post(process.env.OIDC_TOKEN_URL_WITH_BASE_PATH, querystring.stringify(parameters)); - expect(response).toBeDefined(); - }); -}); From 59158dec7e45ab5ff671ee377632ec0d3d6b2418 Mon Sep 17 00:00:00 2001 From: AleF83 Date: Thu, 5 Aug 2021 14:37:00 +0300 Subject: [PATCH 3/5] Tests --- e2e/.env | 2 ++ e2e/docker-compose.yml | 4 ++++ e2e/tests/base-path.spec.ts | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 e2e/tests/base-path.spec.ts diff --git a/e2e/.env b/e2e/.env index 0a6334d..b96c24e 100644 --- a/e2e/.env +++ b/e2e/.env @@ -5,3 +5,5 @@ OIDC_USERINFO_URL=http://localhost:8080/connect/userinfo OIDC_GRANTS_URL=http://localhost:8080/grants OIDC_DISCOVERY_ENDPOINT_HTTPS=https://localhost:8443/.well-known/openid-configuration OIDC_MANAGE_USERS_URL=http://localhost:8080/api/v1/user +OIDC_DISCOVERY_ENDPOINT_WITH_BASE_PATH=http://localhost:8080/some-base-path/.well-known/openid-configuration +OIDC_TOKEN_URL_WITH_BASE_PATH=http://localhost:8080/some-base-path/connect/token diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 052de71..f916011 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -16,6 +16,10 @@ services: USERS_CONFIGURATION_PATH: /config/user-configuration.json CLIENTS_CONFIGURATION_PATH: /config/clients-configuration.json IDENTITY_RESOURCES_PATH: /config/identity-resources.json + ASPNET_SERVICES_OPTIONS_INLINE: | + { + "BasePath": "/some-base-path" + } volumes: - ./config:/config:ro - ./https:/https:ro diff --git a/e2e/tests/base-path.spec.ts b/e2e/tests/base-path.spec.ts new file mode 100644 index 0000000..563ba1c --- /dev/null +++ b/e2e/tests/base-path.spec.ts @@ -0,0 +1,33 @@ +import * as querystring from 'querystring'; +import * as dotenv from 'dotenv'; +import axios from 'axios'; + +import clients from '../config/clients-configuration.json'; +import type { Client } from '../types'; + +describe('Base path', () => { + let client: Client; + + beforeAll(() => { + dotenv.config(); + client = clients.find(c => c.ClientId === 'client-credentials-flow-client-id'); + expect(client).toBeDefined(); + }); + + test('Discovery Endpoint', async () => { + const response = await axios.get(process.env.OIDC_DISCOVERY_ENDPOINT_WITH_BASE_PATH); + expect(response.data.token_endpoint).toEqual(process.env.OIDC_TOKEN_URL_WITH_BASE_PATH); + }); + + test('Token Endpoint', async () => { + const parameters = { + client_id: client.ClientId, + client_secret: client.ClientSecrets?.[0], + grant_type: 'client_credentials', + scope: client.AllowedScopes.join(' '), + }; + + const response = await axios.post(process.env.OIDC_TOKEN_URL_WITH_BASE_PATH, querystring.stringify(parameters)); + expect(response).toBeDefined(); + }); +}); From 54a0a0731a463ec863e966151dfa21bcecf8309b Mon Sep 17 00:00:00 2001 From: AleF83 Date: Thu, 5 Aug 2021 14:37:14 +0300 Subject: [PATCH 4/5] Docs --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 6850823..2804089 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,13 @@ There are two ways to provide configuration for supported scopes, clients and us * `API_RESOURCES_PATH` * `IDENTITY_RESOURCES_PATH` + +## Base path + +The server can be configured to run with base path. So all the server endpoints will be also available with some prefix segment. +For example `http://localhost:8080/my-base-path/.well-known/openid-configuration` and `http://localhost:8080/my-base-path/connect/token`. +Just set `BasePath` property in `ASPNET_SERVICES_OPTIONS_INLINE/PATH` env var. + ## Custom endpoints ### User management From 4cead76a28072d65341947d7c90012a018a600ab Mon Sep 17 00:00:00 2001 From: AleF83 Date: Thu, 5 Aug 2021 14:37:29 +0300 Subject: [PATCH 5/5] eslint fixed --- e2e/.eslintrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e/.eslintrc b/e2e/.eslintrc index 536bb40..7ada225 100644 --- a/e2e/.eslintrc +++ b/e2e/.eslintrc @@ -31,7 +31,8 @@ "unicorn/numeric-separators-style": "off", "unicorn/prevent-abbreviations": ["error", { "checkFilenames": false - }] + }], + "unicorn/prefer-node-protocol": "off" }, "overrides": [ {