Integration guide¶
Overview¶
Sequel Security Services aim is provide a single master domain for managing security concerns, shared across all our frontend applications (at least), open to connect to other systems and based on modern and secured standards.
With this goal, we have defined a set of services in charge of managing security (Authentication and Authorization) based on oAuth 2.0 and OpenID Connect protocols.
This will allow us to decouple services in terms of security, while providing a SSO experience to the user and reducing duplicates effort in different teams for implementing security topics.
We will cover topics like:
- Authentication
- Register API Resources and Clients.
- Single Sign in and Single Sign out.
- Authorization
- Securables, Roles and Groups
- User Types
- Users
- Entities
It is highly recommended to be familiar with other Security documentation:
- Technical documentation
- Platform Specification
- Installation Guide
This document will try to define the steps to integrate the new Sequel Security Model and Services in a web application or Web API.
During this document we will use as sample the Security integration into Sequel Rating Engine.
Design¶
Application¶
As described in the Technical Documentation, the Security Model is driven by the concept of an Application. So, the first step is define how this application will be modelled in terms of authentication and authorization.
All applications needs a Key
that must be unique and short in the sequel ecosystem. Some existing applications (in bold) are and some keys reserve for future (in italic) are:
Application | Application Key |
---|---|
Broking | BRK |
Claims | CLM |
Document Service | DOC |
API Gateway | GWY |
Impact | IMP |
Origin | ORIGIN |
Product Builder | PB |
Rating Engine | SRE |
Reinsurance | RE |
RuleBook | RB |
Security | SEC |
Underwriting | UW |
Workflow | WF |
STEP 1. Naming Decide a name for the application.
In our sample, we have decided to use SRE as the key for the Sequel Rating Engine application.
Authentication¶
Once we have decided a name, we need to model the authentication behaviour of this application. For doing this, we will suggest to prepare a diagram of your application, where all web applications (differentiate UI from server side) and web API are described. Also, existing interactions with other sequel services should be included.
In our sample, in Rating Engine, the diagram will look like:
Based on our architecture we will need to discover the different API resources and clients we need to protect and we need to allow to access; plus the scopes within each resource. Also, we will provide names. For naming authentication components (resources, scopes, clients,..) we have decided to follow the name convention that all those concepts starting by an application key are owned by this application and will be managed in the import/export process as part of this application. So, you have to follow this name convention.
STEP 2. Design Authentication Determine the resources to be protected and the potential clients.
For our sample, we have discovered:
-
2 Resources that needs to be protected:
sre.resource.app
(with a single scopesre.resource.app.user_login
) andsre.resource.api
(with a single scopesre.resource.api.full_access
). -
2 known clients:
sre.client.app
,sre.client.api
and other existing clients.
API Resources¶
We have divided the IIS application in two resources because our application have two clear areas to be protected in different ways. We have decided to model it with two different resources because, ideally, they should be two independent resources
-
Web MVC (that we will called
sre.resource.app
) covers the Rating Engine MVC endpoints that offer service to the web application executed in the browser (this is the clientsre.client.app
). Basically, here the user is a human and how this works is: -
Users using the
sre.client.app
in theirs browsers call tosre.resource.app
. sre.resource.app
asks for authentication tosre.client.app
and user is redirected to the Authentication Server in order to authenticate himself and allow tosre.client.app
to interact withsre.resource.app
in his name.
This is modelled using the hybrid grant type flow; that allows to support access tokens and refresh tokens. This is valid for MVC applications; pure SPA needs to use authorization code flow. In the first versions we were using implicit flow, that is no longer recommended due to security risks: The state of implicit flow
- Web API (
sre.resource.api
) covers the Rest API exposed by Rating Engine and consumed by other services like Portal, Origin or Workflow. In this scenario, the client is a service and security is based on client credentials flow.
Clients¶
- Rating Engine Application Client (
sre.client.app
) executed in browser (HTML+JavaScript). This HTML client will call to thesre.resource.app
. - Rating Engine API Client (
sre.client.api
). This is a proxy provided as a NuGet package for calling to Rating Engine Web API (sre.resource.api
). - Other existing clients. In this specific case; we have decided to provide to Rating Engine the ability of being deployed with the new security enabled or disabled; so, it is backward compatible with existing clients. However, it will no be recommended to keep using it without security enabled. For those clients, it will be required to integrate: register clients and update clients to manage access tokens.
Authorization¶
The authorization model allows to an application to define the securables for protecting resources, the groups and roles.
Communication with Authorization server is provided by Sequel Security Integration NuGets:
Clients¶
- Rating Engine Security Client (
sre.client.security
). Used by Secure attribute for calling Security Authorization API (sec.authorization
,sec.securityApi
).
Securables¶
A "securable" defines a resource or action that requires special authorization to be accessed or executed. There are mainly two types of securables: the built-in securables defined in the system, and securables created by the user. The built-in securables are the ones we need to define at this moment.
There are some patterns that we have been following for creating securables. In general, we have found different scenarios where securables are applied:
- Protect APIs. Based on the maturity of the API (see Richardson Maturity Model) we are protecting we can apply below rules:
- Level-2, HTTP verbs:
- Define one securable per resource
- Assign action to http verb for pure :
- POST - Create action
- PUT - Update action
- GET - Read action
- DELETE - Delete action
- Level-1, API resources:
- Define one securable per resource
- Determine which is the right securable action for each action.
- Level-0, RPC under HTTP. This is when we are using HTTP as a transport system for remote interactions, but without using any of the mechanisms of the web.
- Lack of order forces to determine action per action the proper securable and action associated.
- Protect data. In other scenario, we want to apply some specific securables to the data itself, not to the action. A sample of this is in Workflow, where you can assign a specific securable to a given workflow task, transition,... In this scenario, there are no rules and must be a decision that each analysis will take while designing the security model for the application.
STEP 3. Design Authorization: Securables Define securables required for the Vanilla configuration.
After analysing the Rating Engine requirements, we have detected that we just need to protect some APIs. So, we have listed all controllers and actions
RatingEngineController
Action controller | Verb | Allow Anonymous | Securable | Action |
---|---|---|---|---|
AccessDenied | GET | TRUE | - | - |
Index | Engine | Read | ||
Details | Engine | Read | ||
Promote | EnginePromote | Update | ||
Create | Engine | Create | ||
Create | POST | Engine | Create | |
Edit | Engine | Update | ||
Edit | POST | Engine | Update | |
Delete | Engine | Delete | ||
DeleteConfirmed | POST | Engine | Delete |
EngineModelController
Action controller | Verb | Allow Anonymous | Securable | Action |
---|---|---|---|---|
Details | Model | Read | ||
Help | Model | Read | ||
Create | POST | Model | Create | |
Download | Model | Read | ||
Edit | Model | Update | ||
Edit | POST | Model | Update | |
Delete | Model | Delete | ||
DeleteConfirmed | POST | Model | Delete | |
Execute | POST | ModelManualTest | Read |
WhatIfController (aka Analytics)
Action controller | Verb | Allow Anonymous | Securable | Action |
---|---|---|---|---|
Index | Analytics | Read | ||
CompareList | POST | Analytics | Read | |
Compare | Analytics | Read | ||
GetAllDataCompare | Analytics | Read | ||
GetAllData | Analytics | Read |
Based on this analysis, the securables that we will require for Sequel Rating Engine are:
Key | Name | Create Allowed | Read Allowed | Update Allowed | Delete Allowed | |
---|---|---|---|---|---|---|
SRE.Engine | Engine | Yes | Yes | Yes | Yes | |
SRE.EnginePromote | Engine promotion | Promotes to live | No | No | Yes | No |
SRE.Model | Engine model | Yes | Yes | Yes | Yes | |
SRE.ModelManualTest | Engine model manual test | No | Yes | No | No | |
SRE.Analytics | Analytics | No | Yes | No | No |
TIP In some applications, we have developed unit test to ensure all actions at all controllers are protected as expected.
Groups and Roles¶
Groups are used for defining ACLs; while Roles defines set of permissions. At design time, we need to understand the ACLs requirements of our application and also the different basic roles required by our application. At this stage, we are looking at defining a Vanilla configuration for our application; a base configuration that it will be valid for all implementations. Later, this configuration can be customized by the clients or our support team; however, providing a zero configuration required the first day that you will run your application is required.
There are some requirements and also some common patterns used in Workflow, Product Builder, Origin and Claims that we describe below:
-
Public group. All applications are required to define a
[ApplicationKey].Public
group as this is the default value used by the Security Service when the group is not provided. In general, existing application are protecting all endpoints offered to UI base clients with the[ApplicationKey].Public
group. -
Roles. Some basic roles are provided for each application to facilitate the configuration; like:
[ApplicationKey].Configurator
or[ApplicationKey].Standard
. But, this is up to each product team to define it.
STEP 4. Design Authorization: Groups & Roles Define groups required for the Vanilla configuration.
In our sample, we will keep it simple and we will define a SRE.Public
group and a SRE.Configurator
and SRE.Tester
roles.
SRE.Public
group: At Sequel Rating Engine there is no concept of ACLs, so having a single group is enough to cover our requirements.
SRE.Configurator
role: Models a full access administrator user; this is the single and current role that exists at rating engine. Permissions for this role will be:
Key | Create | Read | Update | Delete |
---|---|---|---|---|
SRE.Engine | Yes | Yes | Yes | Yes |
SRE.EnginePromote | Yes | |||
SRE.Model | Yes | Yes | Yes | Yes |
SRE.ModelManualTest | Yes | |||
SRE.Analytics | Yes |
SRE.Tester
role: Models a new role that represents a user that cannot create or change ratings or rating models; and the action that can do is execute manual test of rating engines (so, also can read information). Permissions for this role will be:
Key | Create | Read | Update | Delete |
---|---|---|---|---|
SRE.Engine | Yes | |||
SRE.Model | Yes | |||
SRE.ModelManualTest | Yes |
User Types¶
User Types provides a mechanism for grouping users; allowing to assign a user to a maximum of one user type per application. There are no rules related to security or permissions associated to user types.
Create security package definition¶
STEP 5. Create a security package definition for the application.
Overview¶
Security configuration can be imported and exported using a format based on folder and json files:
- [ApplicationKey] folder. Root folder, named with the application key
- Application.json. File with application definition.
- Authentication folder. Authentication folder contains all settings related to resources and clients; it's highly dependant of the environment where it will be deployed, as contains URI relative to this environment.
- ApiResources.json. File with definition of API resources.
- Clients.json. File with definition of clients.
- Authorization folder. Contains all settings for authorization concepts like:
- Groups.json.
- Roles.json.
- Securables.json.
- UserTypes.json.
Application¶
Create a folder and named with the ApplicationKey; in our sample SRE.
Inside this new folder, add a new file called Application.json and populate with below information:
{
"Key": "[ApplicationKey]",
"Code": "[ApplicationKey]",
"Name": "[ApplicationName]"
}
In our sample, for Rating Engine; this file will look like:
{
"Key": "SRE",
"Code": "SRE",
"Name": "Sequel Rating Engine"
}
- ApplicationKey: Unique key to refer an application. Key and Code must contain the same value.
- ApplicationName: More human friendly name used in UI when referring to this application
Authentication¶
Under the application folder create a new folder Authentication
. The files we need to model in this section have been defined to easily import and export information required by Identity Server to work.
API Resources: ApiResources.json¶
At the Authentication
folder create a new file ApiResources.json
. In this file, we will model the resources we want to protect in our service. This file contains a list of Api resources
definition. And API resource object looks like:
{
"Enabled": true,
"Name": [ApiResourceName],
"DisplayName": [ApiResourceDisplayName],
"Description": [ApiResourceDescription],
"UserClaims": [],
"ApiSecrets": [],
"Scopes": [
{
"Name": [ScopeName],
"DisplayName": [ScopeDisplayName],
"Description": [ScopeDescription],
"Required": true,
"Emphasize": true,
"ShowInDiscoveryDocument": true,
"UserClaims": []
}
]
}
For defining a resource we need:
- ApiResourceName: Unique name assigned to this resource. Keep in mind the name convention.
- ApiResourceDisplayName: Display name for the resource. Not used yet, but we will use in future when the Security Administration UI will allow to manage resources.
- ApiResourceDescription: Description of the resource. Not used yet
And, for each scope:
- ScopeName: Unique name assigned to this scope. Keep in mind the name convention.
- ScopeDisplayName: Display name for the scope. Not used yet, but we will use in future when the Security Administration UI will allow to manage scopes.
- ScopeDescription: Description of the scope. Not used yet
For our sample in Rating Engine, the file will look like:
[
{
"Enabled": true,
"Name": "sre.resource.api",
"DisplayName": "Rating Engine API resource",
"Description": "Rating Engine API resource",
"UserClaims": [],
"ApiSecrets": [],
"Scopes": [
{
"Name": "sre.resource.api.full_access",
"DisplayName": "Full access to Rating Engine API",
"Description": "Full access to Rating Engine API",
"Required": true,
"Emphasize": true,
"ShowInDiscoveryDocument": true,
"UserClaims": []
}
]
},
{
"Enabled": true,
"Name": "sre.resource.app",
"DisplayName": "Rating Engine Application resource",
"Description": "Rating Engine Application resource",
"UserClaims": [],
"ApiSecrets": [],
"Scopes": [
{
"Name": "sre.resource.app.user_login",
"DisplayName": "User login to Rating Engine Application",
"Description": "User login to Rating Engine Application",
"Required": true,
"Emphasize": true,
"ShowInDiscoveryDocument": true,
"UserClaims": []
}
]
}
]
Clients: Clients.json¶
At the Authentication
folder create a new file Clients.json
. In this file, we will model the client we want to allow access to our service as part of our Vanilla configuration; keep it mind that later when designing a given solution other clients could be required and can be added. Configuring clients is so far the most complex task we will need to tackle in this process.
Currently, we have been configuring four types of clients that we will describe in the next sections and we will us patterns for configuring new ones. In general, those clients are based on different grant types (flows). It's true there is no limitation to allow the same client to use different grant types; however, in general, we have been using a single grant type per client.
Before going into the details, we will describe the scenarios we have already covered:
SPA web application: based on authorization code flow¶
In a SPA application we have a browser-based javascript client for user authentication and delegated access. This client uses the so called authorization code flow (recommended with PKCE
enabled) to request an access token from Javascript. In this type of clients the user will login to IdentityServer, invoke the web API with an access token issued by IdentityServer, and logout of IdentityServer. All of this will be driven from the JavaScript running in the browser with the help of the oidc-client
, or any other library that implements the OpenID Connect protocol.
As a sample, we recommend to look at Security Admin site, as this is a pure SPA done with React.
More information at:
Identity Server documentation about Adding a JavaScript client
MVC web application: based on hybrid flow¶
Interactive server side (like MVC) applications use the hybrid flow. This flow gives you the best security because the access tokens are transmitted via back-channel calls only (and gives you access to refresh tokens). Our sample uses this system and we provide NuGet packages for integrate it easily.
APIs for machine-to-machine communications: based on client_credentials flow¶
In this scenario no interactive user is present - a service (aka client) wants to communicate with an API (aka scope). The flow type used in client credentials
. The client interacts with the API using a client id and a secret.
Swagger¶
Most of our API have been configured to use Swagger; so we have to consider Swagger a client: it will depend the type of API we are accessing we will have to configure this client for using a flow type based on user interaction (implicit or hybrid) or no interactive user (client credentials)
The Sequel Rating Engine clients.json¶
For our sample, the clients file will look like:
[
{
"BackChannelLogoutSessionRequired": true,
"AlwaysIncludeUserClaimsInIdToken": true,
"IdentityTokenLifetime": 300,
"AccessTokenLifetime": 3600,
"AuthorizationCodeLifetime": 300,
"AbsoluteRefreshTokenLifetime": 2592000,
"SlidingRefreshTokenLifetime": 1269600,
"ConsentLifetime": null,
"RefreshTokenUsage": 1,
"UpdateAccessTokenClaimsOnRefresh": false,
"RefreshTokenExpiration": 1,
"AccessTokenType": 0,
"EnableLocalLogin": true,
"IdentityProviderRestrictions": [],
"IncludeJwtId": true,
"Claims": [],
"AlwaysSendClientClaims": true,
"ClientClaimsPrefix": "client_",
"PairWiseSubjectSalt": null,
"AllowedScopes": [
"sre.resource.api.full_access"
],
"AllowOfflineAccess": true,
"Properties": {},
"BackChannelLogoutUri": null,
"Enabled": true,
"ClientId": "sre.client.api",
"ProtocolType": "oidc",
"ClientSecrets": [
{
"Description": "Rating Engine API",
"Value": "__ClientSecretHash__",
"Expiration": null,
"Type": "SharedSecret"
}
],
"RequireClientSecret": false,
"ClientName": "Rating Engine API",
"ClientUri": null,
"LogoUri": null,
"AllowedCorsOrigins": [],
"RequireConsent": false,
"AllowedGrantTypes": [
"client_credentials"
],
"RequirePkce": false,
"AllowPlainTextPkce": false,
"AllowAccessTokensViaBrowser": true,
"RedirectUris": [],
"PostLogoutRedirectUris": [],
"FrontChannelLogoutUri": null,
"FrontChannelLogoutSessionRequired": true,
"AllowRememberConsent": true
},
{
"BackChannelLogoutSessionRequired": true,
"AlwaysIncludeUserClaimsInIdToken": true,
"IdentityTokenLifetime": 300,
"AccessTokenLifetime": 300,
"AuthorizationCodeLifetime": 300,
"AbsoluteRefreshTokenLifetime": 0,
"SlidingRefreshTokenLifetime": 1296000,
"ConsentLifetime": null,
"RefreshTokenUsage": 1,
"UpdateAccessTokenClaimsOnRefresh": false,
"RefreshTokenExpiration": 0,
"AccessTokenType": 0,
"EnableLocalLogin": true,
"IdentityProviderRestrictions": [],
"IncludeJwtId": true,
"Claims": [],
"AlwaysSendClientClaims": true,
"ClientClaimsPrefix": "client_",
"PairWiseSubjectSalt": null,
"AllowedScopes": [
"openid",
"profile",
"sre.resource.app.user_login"
],
"AllowOfflineAccess": true,
"Properties": {},
"BackChannelLogoutUri": null,
"Enabled": true,
"ClientId": "sre.client.app",
"ProtocolType": "oidc",
"ClientSecrets": [
{
"Description": "Rating Engine",
"Value": "__ClientSecretHash__",
"Expiration": null,
"Type": "SharedSecret"
}
],
"RequireClientSecret": true,
"ClientName": "Rating Engine Application",
"ClientUri": null,
"LogoUri": null,
"AllowedCorsOrigins": [],
"RequireConsent": false,
"AllowedGrantTypes": [
"hybrid"
],
"RequirePkce": false,
"AllowPlainTextPkce": false,
"AllowAccessTokensViaBrowser": true,
"RedirectUris": [
"__RatingEngineUrlExternal__/signin-oidc"
],
"PostLogoutRedirectUris": [
"__RatingEngineUrlExternal__/signout-callback-oidc"
],
"FrontChannelLogoutUri": null,
"FrontChannelLogoutSessionRequired": true,
"AllowRememberConsent": true
},
{
"BackChannelLogoutSessionRequired": true,
"AlwaysIncludeUserClaimsInIdToken": true,
"IdentityTokenLifetime": 300,
"AccessTokenLifetime": 3600,
"AuthorizationCodeLifetime": 300,
"AbsoluteRefreshTokenLifetime": 2592000,
"SlidingRefreshTokenLifetime": 1269600,
"ConsentLifetime": null,
"RefreshTokenUsage": 1,
"UpdateAccessTokenClaimsOnRefresh": false,
"RefreshTokenExpiration": 1,
"AccessTokenType": 0,
"EnableLocalLogin": true,
"IdentityProviderRestrictions": [],
"IncludeJwtId": true,
"Claims": [],
"AlwaysSendClientClaims": true,
"ClientClaimsPrefix": "client_",
"PairWiseSubjectSalt": null,
"AllowedScopes": [
"sec.api",
"sec.authorization"
],
"AllowOfflineAccess": true,
"Properties": {},
"BackChannelLogoutUri": null,
"Enabled": true,
"ClientId": "sre.client.security",
"ProtocolType": "oidc",
"ClientSecrets": [],
"RequireClientSecret": false,
"ClientName": "Rating Engine Authorization ",
"ClientUri": null,
"LogoUri": null,
"AllowedCorsOrigins": [],
"RequireConsent": false,
"AllowedGrantTypes": [
"client_credentials"
],
"RequirePkce": false,
"AllowPlainTextPkce": false,
"AllowAccessTokensViaBrowser": true,
"RedirectUris": [],
"PostLogoutRedirectUris": [],
"FrontChannelLogoutUri": null,
"FrontChannelLogoutSessionRequired": true,
"AllowRememberConsent": true
}
]
Authorization¶
As described at the beginning of this section, we need to define four files with Securables, Groups, Roles and User Types for the Vanilla configuration of our application.
Under the application folder create a new folder Authorization
.
TIP: Authorization configuration can be modelled using Administration UI; exporting the configuration and later marking all entries as IsSystem.
Securables¶
The Securables.json
contains a list of securable objects:
{
"Key": [SecurableKey],
"ApplicationKey": [ApplicationKey],
"Code": [Code],
"Name": [Name],
"Description": [Description],
"IsSystem": true,
"IsCreateAllowed": [IsCreateAllowed]],
"CreateDescription": [CreateDescription],
"IsReadAllowed": [IsReadAllowed],
"ReadDescription": [ReadDescription],
"IsUpdateAllowed": [IsUpdateAllowed],
"UpdateDescription": [UpdateDescription],
"IsDeleteAllowed": [IsDeleteAllowed],
"DeleteDescription": [DeleteDescription]
}
As all fields have been already described above, we will go directly to describe this file for Rating Engine, the securable file will look like:
[
{
"Key": "SRE.Engine",
"ApplicationKey": "SRE",
"Code": "Engine",
"Name": "Engine",
"Description": "Engine",
"IsSystem": true,
"IsCreateAllowed": true,
"CreateDescription": null,
"IsReadAllowed": true,
"ReadDescription": null,
"IsUpdateAllowed": true,
"UpdateDescription": null,
"IsDeleteAllowed": true,
"DeleteDescription": null
},
{
"Key": "SRE.EnginePromote",
"ApplicationKey": "SRE",
"Code": "EnginePromote",
"Name": "Engine Promote",
"Description": "Promote engine models to live",
"IsSystem": true,
"IsCreateAllowed": false,
"CreateDescription": null,
"IsReadAllowed": false,
"ReadDescription": null,
"IsUpdateAllowed": true,
"UpdateDescription": null,
"IsDeleteAllowed": false,
"DeleteDescription": null
},
{
"Key": "SRE.Model",
"ApplicationKey": "SRE",
"Code": "Model",
"Name": "Engine models",
"Description": "Engine models",
"IsSystem": true,
"IsCreateAllowed": true,
"CreateDescription": null,
"IsReadAllowed": true,
"ReadDescription": null,
"IsUpdateAllowed": true,
"UpdateDescription": null,
"IsDeleteAllowed": true,
"DeleteDescription": null
},
{
"Key": "SRE.ModelManualTest",
"ApplicationKey": "SRE",
"Code": "ModelManualTest",
"Name": "Engine Model manual test",
"Description": "Allows to test models manually from administration site",
"IsSystem": true,
"IsCreateAllowed": false,
"CreateDescription": null,
"IsReadAllowed": true,
"ReadDescription": null,
"IsUpdateAllowed": false,
"UpdateDescription": null,
"IsDeleteAllowed": false,
"DeleteDescription": null
},
{
"Key": "SRE.Analytics",
"ApplicationKey": "SRE",
"Code": "Analytics",
"Name": "Analytics",
"Description": "Analytics",
"IsSystem": true,
"IsCreateAllowed": false,
"CreateDescription": null,
"IsReadAllowed": true,
"ReadDescription": null,
"IsUpdateAllowed": false,
"UpdateDescription": null,
"IsDeleteAllowed": false,
"DeleteDescription": null
}
]
Groups¶
The group.json
file contains a list of groups; a group is defined as:
[
{
"Key": [Key],
"ApplicationKey": [ApplicationKey],
"Code": [Code],
"Name": [Name],
"AutomaticallyCreated": false,
"IsSystem": true
}
]
Fields in the group object have been already defined.
For Sequel Rating Engine, the group file will look like:
[
{
"Key": "SRE.Public",
"ApplicationKey": "SRE",
"Code": "Public",
"Name": "Public",
"AutomaticallyCreated": false,
"IsSystem": true
}
]
Roles¶
At roles files, we will define roles and also the list of permissions. For Rating Engine, this will look like:
[
{
"Key": "SRE.Configurator",
"ApplicationKey": "SRE",
"Code": "Configurator",
"Name": "Configurator",
"IsSystem": true,
"Permissions": [
{
"SecurableKey": "SRE.Engine",
"Create": true,
"Read": true,
"Update": true,
"Delete": true
},
{
"SecurableKey": "SRE.EnginePromote",
"Create": false,
"Read": true,
"Update": false,
"Delete": false
},
{
"SecurableKey": "SRE.Model",
"Create": true,
"Read": true,
"Update": true,
"Delete": true
},
{
"SecurableKey": "SRE.ModelManualTest",
"Create": false,
"Read": true,
"Update": false,
"Delete": false
},
{
"SecurableKey": "SRE.Analytics",
"Create": false,
"Read": true,
"Update": false,
"Delete": false
}
]
},
{
"Key": "SRE.Tester",
"ApplicationKey": "SRE",
"Code": "Tester",
"Name": "Tester",
"IsSystem": true,
"Permissions": [
{
"SecurableKey": "SRE.Engine",
"Create": false,
"Read": true,
"Update": false,
"Delete": false
},
{
"SecurableKey": "SRE.Model",
"Create": false,
"Read": true,
"Update": false,
"Delete": false
},
{
"SecurableKey": "SRE.ModelManualTest",
"Create": false,
"Read": true,
"Update": false,
"Delete": false
}
]
}
]
User Types¶
User types is only used by Origin application. An empty file looks like:
[]
Import package to security service¶
Once we have defined the files for configuring authentication and authorization following the described folder schema we can import them using the sequel-security
tool.
Configure Security¶
Security configuration is provided by several NuGet packages:
- Sequel Security Integration (
Sequel.Security.Integration
) contains methods for create ISecuritySettings configuration that will be use by the rest of the package. - Sequel Security Integration OWIN (
Sequel.Security.Integration.Owin
) contains methods for setting up authentication schemas (Cookie and Bearer) in applications using OWIN. - Sequel Security Integration NetCore (
Sequel.Security.Integration.NetCore
) contains methods for setting up authentication schema (Cookie and Bearer) in net core applications. - Sequel Security Integration NetCore Http (
Sequel.Security.Integration.NetCore.Http
) contains methods for setting up the authorization aspect in net core applications. - User Session Avatar (
@sequel/sequel.web.usersessionavatar
) contains extended session management and a nice looking user avatar for your web app.
Creating Security Settings¶
First of all it's necessary to create ISecuritySettings interface with the security configuration the application. Sequel Security Integration provided all methods for it.
For Rating Engine the configuration is stored in the appsettings.json:
"SecuritySettings": {
"IsSecurityEnabled": true,
"PermissionsCacheDurationInMinutes": 5,
"ApplicationSettings": {
"InternalUrl": [######],
"ExternalUrl": [######]
},
"SessionSettings": {
"TimeoutInMinutes": 15,
"TimeoutWarningInMinutes": 3
},
"AuthorizationServerSettings": {
"InternalUrl": [######],
"ExternalUrl": [######]
},
"AuthenticationServerSettings": {
"InternalUrl": [######],
"ExternalUrl": [######]
},
"CookieSettings": {
"IsSecureModeEnabled": true
},
"RatingEngineApplicationClient": {
"ClientId": "sre.client.app",
"ClientSecret": [######],
"Scopes": "sre.resource.app.user_login"
},
"RatingEngineSecurityClient": {
"ClientId": "sre.client.security",
"ClientSecret": [######],
"Scopes": "sec.api sec.authorization"
},
"RatingEngineApi": {
"ApiName": "sre.resource.api"
}
}
Configuration is created reading that configuration and executing next methods:
protected ISecuritySettings GetSecuritySettings(Settings.Security.RatingEngineSecuritySettings ratingEngineSecuritySettings)
{
var ratingEngineApplicationClient = SecurityClientSettingsExtensions
.CreateClientSettings(ratingEngineSecuritySettings.RatingEngineApplicationClient.ClientId, ratingEngineSecuritySettings.RatingEngineApplicationClient.ClientSecret)
.IncludeScopes(ratingEngineSecuritySettings.RatingEngineApplicationClient.Scopes)
.IncludeDefaultRedirections(ratingEngineSecuritySettings.ApplicationSettings.InternalUrl, ratingEngineSecuritySettings.ApplicationSettings.ExternalUrl);
var ratingEngineAuthorizationClient = SecurityClientSettingsExtensions
.CreateClientSettings(ratingEngineSecuritySettings.RatingEngineSecurityClient.ClientId, ratingEngineSecuritySettings.RatingEngineSecurityClient.ClientSecret)
.IncludeScopes(ratingEngineSecuritySettings.RatingEngineSecurityClient.Scopes)
.IncludeDefaultRedirections(ratingEngineSecuritySettings.ApplicationSettings.InternalUrl, ratingEngineSecuritySettings.ApplicationSettings.ExternalUrl);
var ratingEngineApiResource = SecurityApiResourceSettingsExtensions.CreateApiResourceSettings(ratingEngineSecuritySettings.RatingEngineApi.ApiName);
var securitySettings = SecuritySettingsExtensions.UseSecuritySettings(ratingEngineSecuritySettings.ApplicationSettings.ApplicationKey)
.AddSessionSettings(
timeoutInMinutes: ratingEngineSecuritySettings.SessionSettings.TimeoutInMinutes,
timeoutWarningInMinutes: ratingEngineSecuritySettings.SessionSettings.TimeoutWarningInMinutes)
.AddCookieSettings(
ssoCookieDomain: string.Empty,
isSecureModeEnabled: ratingEngineSecuritySettings.CookieSettings.IsSecureModeEnabled)
.AddAuthorizationSettings(
authorizationServerUri: ratingEngineSecuritySettings.AuthorizationServerSettings.InternalUrl,
externalAuthorizationServerUri: ratingEngineSecuritySettings.AuthorizationServerSettings.ExternalUrl)
.AddAuthenticationSettings(
authenticationServerUri: ratingEngineSecuritySettings.AuthenticationServerSettings.InternalUrl,
externalAuthenticationServerUri: ratingEngineSecuritySettings.AuthenticationServerSettings.ExternalUrl)
.AddClientSettings(ratingEngineApplicationClient)
.AddClientSettings(ratingEngineAuthorizationClient)
.AddApiResourceSettings(ratingEngineApiResource)
.ActivateAuthenticationConditionally(ratingEngineSecuritySettings.IsSecurityEnabled)
.ActivateAuthorizationConditionally(ratingEngineSecuritySettings.IsSecurityEnabled);
return securitySettings;
}
Authentication middleware¶
During application start up we need to configure the middleware which will ensure the identity of the requester and will force the redirections to Authentication server for authentications. It's mandatory create a ISecuritySettings instance before to be able to use all the methods.
.net framework applications¶
Sequel.Security.Integration.Owin
contains method for configure authentication for MVC and Web API controllers in applications on .NET framework using OWIN.
We can protect controllers using Cookie Authentication (using user/password as challenge method) indicating the Client and the context's conditions to accomplish and, in a similar way, we can protect controllers using Bearer Authentication indicating the Api Name and the the context's conditions to accomplish.
Supposing that Rating Engine were a net framework application with OWIN we could do this this way:
// app is IAppBuilder
app.UseSequelSecurity(securitySettings)
.UseCookieAuthentication(applicationSettings.SecuritySettings.RatingEngineApplicationClient.ClientId, context => !context.Request.Path.Value.ToLower().Contains("/api/"))
.UseBearerAuthentication(applicationSettings.SecuritySettings.RatingEngineApi.ApiName)
.net core applications¶
Sequel.Security.Integration.NetCore
contains method for configure authentication schemas for MVC and Web API controllers.
In Sequel Rating Engine it's used Cookie Authentication (using user/password as challenge method) for MVC controllers and Bearer token authentication for Web API.
// startup.cs
services.AddSequelSecurityAuthentication(securitySettings)
.AddCookieAuthentication(applicationSettings.SecuritySettings.RatingEngineApplicationClient.ClientId)
.AddBearerAuthentication(AuthenticationConstants.BearerAuthenticationScheme, applicationSettings.SecuritySettings.RatingEngineApi.ApiName);
.AddSequelSecurityAuthorization(securitySettings);
For MVC controllers:
[Authorize(Policy = AuthorizationConstants.IsSecurityEnabledPolicy)]
[Authorize]
public abstract class SecuredMvcController : Controller
{
}
For API controllers:
[Authorize(Policy = AuthorizationConstants.IsSecurityEnabledPolicy)]
[Authorize(AuthenticationSchemes = AuthenticationConstants.BearerAuthenticationScheme)]
public abstract class ApiControllerBase : ControllerBase
{
}
Single sign-in & sign-out¶
For a better single sign-in and sign-out experience all applications must be deployed in the same domain. As we will be able to automatically detect single sing-in and out events and force the application to react; all information about this subject can be found in Single Sign-Out document. Also checkout User Session Avatar documentation for SPAs or similar web apps.
When applications are not in the same domain, single sign-out is not possible; and single sign-in works; however, we can not detect sign-in and out events from other applications.
Authorization¶
During application start up we need to make additional configuration for interactions with Authorization server and be able to check current user permissions.
For Rating Engine this methods are included in Sequel.Security.Integration.NetCore.Http
. It's necessary add the client that will be used for communications with Authorization server, enable Permissions' cache to improve the performance and a page for redirect users when they haven't the permissions required:
// startup.cs
services
...
.AddSequelSecurityAuthorization(securitySettings)
.AddSecureAttribute(applicationSettings.SecuritySettings.RatingEngineSecurityClient.ClientId)
.AddPermissionsCache(applicationSettings.SecuritySettings.PermissionsCacheDurationInMinutes)
.AddNoAuthorizedAction(new ViewResult { ViewName = "NoAuthorized" });
Consuming effective permissions¶
For consuming effective permissions it's necessary decorate controllers/methods with the SecureAttribute
indicating what Securable and Action is required for the access.
In Rating Engine a static class contains all Securable names for a better reference:
public static class Securables
{
public const string Engine = "Engine";
public const string EnginePromote = "EnginePromote";
public const string Model = "Model";
public const string ModelManualTest = "ModelManualTest";
public const string Analytics = "Analytics";
}
[Secure(Securables.Engine, Action.Read)]
public IActionResult Index()
{
}
API¶
Internal APIs just need to be protected for clients using client_credentials flow; and authorization is not required. When an API is publicly exposed (read it in the API gateway) we have to apply authorization based on scopes.
Caching and service bus messages¶
Sequel.Security.Integration.NetCore.Http
contains a cache used by Secure attribute to store for some minutes (5 by default) the responses obtained from Authorization server to know the permissions of a user. That means if there is a permissions change for that user in the Authorization server, applications using this cache won't be notified until cache expiration.
When there is a permissions change for a user, Security API sends a message to the bus notifying this. Applications using it's own implementation of Secure attribute with a cache can use a consumer listening a queue connected to the same exchange that SecurityAPI waiting for a Sequel.Security.MessageBus.Contracts.EffectivePermission.Changed.v1.EffectivePermissionChangedMessage
message for invalidating all cache entries for that user.
It's important to be sure that other consumers (like Authorizations server) are not consuming messages from the same queue to avoid inconsistencies between the permissions obtained for our cache and permissions obtained from Authorization server.
Action filters¶
Applications using custom Secure attribute will have an action filter implementing IAuthorizationFilter to include the logic for get user permissions a accept or reject the request. Request will have two execution flows depending on if is a request from a user or a server. If the request is from another server using a Client Credentials flow the don't have to check for permissions if request if from a user we should get the username from the context, call to Authorization server (we'll need a client to get a token a do the request) and finally using de Authorization server's response check the permissions. The pseudo-code logic will be:
private readonly string action;
private readonly string securable;
public void OnAuthorizationAsync(AuthorizationFilterContext context)
{
string username= GetUserNameFromContext(context);
if (String.IsNullOrEmpty(username)) //It's a Client Credentials request
return;
var permissions = CallAuthorizationServerForUserPermissions(username);
bool hasPermissions = UserHasPermissionsForSecurableAction(permissions, securable, action);
if (!hasPermissions)
context.Result = GetNotAuthorizedActionResult();
}