Skip to content

Commit

Permalink
Added swagger authentication for identity server (#217)
Browse files Browse the repository at this point in the history
* Added swagger authentication for identity server

* Make things less hardcoded

Co-authored-by: Dmytro M <[email protected]>
  • Loading branch information
Bogdan-Hasanov and DmyMi authored Jul 15, 2021
1 parent 15c16c2 commit 691815d
Show file tree
Hide file tree
Showing 14 changed files with 294 additions and 110 deletions.
30 changes: 30 additions & 0 deletions OutOfSchool/OutOfSchool.DataAccess/Extensions/DbSetExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;

#nullable enable

namespace OutOfSchool.Services.Extensions
{
public static class DbSetExtensions
{
/// <summary>
/// Add entry if it doesn't exist based on a predicate.
/// Not thread safe! There's a window between Any and Add where another thread can add an entry.
/// Performance issues is need to add multiple entities.
/// </summary>
/// <param name="dbSet">Extension target.</param>
/// <param name="entity">Entry to add.</param>
/// <param name="predicate">Optional predicate.</param>
/// <typeparam name="T">Entity type.</typeparam>
/// <returns>Return an entity that was just added or null.</returns>
public static EntityEntry<T>? AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>>? predicate = null)
where T : class, new()
{
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return !exists ? dbSet.Add(entity) : null;
}
}
}
77 changes: 0 additions & 77 deletions OutOfSchool/OutOfSchool.IdentityServer/Config.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace OutOfSchool.IdentityServer.Config
{
public class AdditionalIdentityClients
{
public string ClientId { get; set; }

public string[] RedirectUris { get; set; }

public string[] PostLogoutRedirectUris { get; set; }

public string[] AllowedCorsOrigins { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace OutOfSchool.IdentityServer.Config
{
public class IdentityAccessOptions
{
public readonly string Name = "IdentityAccessConfig";

public AdditionalIdentityClients[] AdditionalIdentityClients { get; set; }
}
}
79 changes: 79 additions & 0 deletions OutOfSchool/OutOfSchool.IdentityServer/Config/StaticConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Linq;
using IdentityServer4;
using IdentityServer4.Models;

namespace OutOfSchool.IdentityServer.Config
{
public static class StaticConfig
{
public static IEnumerable<IdentityResource> IdentityResources =>
new[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResource
{
Name = "role",
UserClaims = new List<string> {"role"},
},
};

public static IEnumerable<ApiScope> ApiScopes =>
new[]
{
new ApiScope("outofschoolapi.read"),
new ApiScope("outofschoolapi.write"),
};

public static IEnumerable<ApiResource> ApiResources(string apiSecret) => new[]
{
new ApiResource("outofschoolapi")
{
Scopes = new List<string> {"outofschoolapi.read", "outofschoolapi.write"},
ApiSecrets = new List<Secret> { new Secret(apiSecret.Sha256()) },
UserClaims = new List<string> {"role"},
},
};

public static IEnumerable<Client> Clients(string clientSecret, IEnumerable<AdditionalIdentityClients> additionalClients)
{
// m2m client credentials flow client
var clients = new List<Client>
{
new Client
{
ClientId = "m2m.client",
ClientName = "Client Credentials Client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = {new Secret(clientSecret.Sha256()) },
AllowedScopes = {"outofschoolapi.read", "outofschoolapi.write"},
},
};

clients.AddRange(additionalClients.Select(c => new Client
{
ClientId = c.ClientId,
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
AllowOfflineAccess = true,

RedirectUris = c.RedirectUris,
PostLogoutRedirectUris = c.PostLogoutRedirectUris,
AllowedCorsOrigins = c.AllowedCorsOrigins,

AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
"outofschoolapi.read", "outofschoolapi.write",
},

AllowAccessTokensViaBrowser = true,
RequireConsent = false,
}));

return clients;
}
}
}
54 changes: 24 additions & 30 deletions OutOfSchool/OutOfSchool.IdentityServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OutOfSchool.IdentityServer;
using OutOfSchool.IdentityServer.Config;
using OutOfSchool.Services;
using OutOfSchool.Services.Extensions;

namespace IdentityServer
namespace OutOfSchool.IdentityServer
{
public class Program
{
Expand All @@ -34,8 +35,12 @@ public static void Main(string[] args)
.Database.Migrate();
var identityContext = scope.ServiceProvider.GetRequiredService<OutOfSchoolDbContext>();
var configService = scope.ServiceProvider.GetRequiredService<IConfiguration>();

// TODO: Move to identity options
var apiSecret = configService["outofschoolapi:ApiSecret"];
var clientSecret = configService["m2m.client:ClientSecret"];
var identityOptions = new IdentityAccessOptions();
configService.GetSection(identityOptions.Name).Bind(identityOptions);

context.Database.Migrate();
identityContext.Database.Migrate();
Expand All @@ -46,45 +51,34 @@ public static void Main(string[] args)
RolesInit(manager);
}

if (!context.Clients.Any())
foreach (var client in StaticConfig.Clients(clientSecret, identityOptions.AdditionalIdentityClients))
{
foreach (var client in Config.Clients(clientSecret))
{
context.Clients.Add(client.ToEntity());
}

context.SaveChanges();
context.Clients.AddIfNotExists(client.ToEntity(), c => c.ClientId == client.ClientId);
}

if (!context.IdentityResources.Any())
{
foreach (var resource in Config.IdentityResources)
{
context.IdentityResources.Add(resource.ToEntity());
}
context.SaveChanges();

context.SaveChanges();
}

if (!context.ApiResources.Any())
foreach (var resource in StaticConfig.IdentityResources)
{
foreach (var resource in Config.ApiResources(apiSecret))
{
context.ApiResources.Add(resource.ToEntity());
}

context.SaveChanges();
context.IdentityResources.AddIfNotExists(resource.ToEntity(), ir => resource.Name == ir.Name);
}

if (!context.ApiScopes.Any())
context.SaveChanges();

foreach (var resource in StaticConfig.ApiResources(apiSecret))
{
foreach (var resource in Config.ApiScopes)
{
context.ApiScopes.Add(resource.ToEntity());
}
context.ApiResources.AddIfNotExists(resource.ToEntity(), ar => resource.Name == ar.Name);
}

context.SaveChanges();

context.SaveChanges();
foreach (var resource in StaticConfig.ApiScopes)
{
context.ApiScopes.AddIfNotExists(resource.ToEntity(), apiScope => apiScope.Name == resource.Name);
}

context.SaveChanges();
}

host.Run();
Expand Down
1 change: 1 addition & 0 deletions OutOfSchool/OutOfSchool.IdentityServer/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public void ConfigureServices(IServiceCollection services)
{
if (env.IsEnvironment("Release"))
{
// TODO: Change this to something decent and configurable
options.IssuerUri = "http://hostname:5443";
}
})
Expand Down
31 changes: 31 additions & 0 deletions OutOfSchool/OutOfSchool.IdentityServer/appsettings.Azure.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,36 @@
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
}
},
"IdentityAccessConfig": {
"AdditionalIdentityClients": [
{
"ClientId": "angular",
"RedirectUris": [
"http://localhost:4200",
"http://oos.dmytrominochkin.cloud"
],
"PostLogoutRedirectUris": [
"http://localhost:4200",
"http://oos.dmytrominochkin.cloud"
],
"AllowedCorsOrigins": [
"http://localhost:4200",
"http://oos.dmytrominochkin.cloud"
]
},
{
"ClientId": "Swagger",
"RedirectUris": [
"https://api.oos.dmytrominochkin.cloud/swagger/oauth2-redirect.html"
],
"PostLogoutRedirectUris": [
"https://api.oos.dmytrominochkin.cloud/swagger/oauth2-redirect.html"
],
"AllowedCorsOrigins": [
"https://api.oos.dmytrominochkin.cloud"
]
}
]
}
}
31 changes: 31 additions & 0 deletions OutOfSchool/OutOfSchool.IdentityServer/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,36 @@
"Username": "",
"Password": ""
}
},
"IdentityAccessConfig": {
"AdditionalIdentityClients": [
{
"ClientId": "angular",
"RedirectUris": [
"http://localhost:4200",
"http://oos.dmytrominochkin.cloud"
],
"PostLogoutRedirectUris": [
"http://localhost:4200",
"http://oos.dmytrominochkin.cloud"
],
"AllowedCorsOrigins": [
"http://localhost:4200",
"http://oos.dmytrominochkin.cloud"
]
},
{
"ClientId": "Swagger",
"RedirectUris": [
"http://localhost:5000/swagger/oauth2-redirect.html"
],
"PostLogoutRedirectUris": [
"http://localhost:5000/swagger/oauth2-redirect.html"
],
"AllowedCorsOrigins": [
"http://localhost:5000"
]
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"OutOfSchool": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "Organization/TestOk",
"launchUrl": "swagger",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
Expand Down
Loading

0 comments on commit 691815d

Please sign in to comment.