Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Identity model docs #463

Merged
merged 14 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@
"version": "0.2.0",
"configurations": [
{
"name": "hugo - v7",
"name": "IdentityServer v7",
"request": "launch",
"type": "f5anything",
"command": "hugo server --navigateToChanged --source ${workspaceFolder}/IdentityServer/v7/docs"
},
{
"name": "hugo - v6",
"name": "IdentityServer v6",
"request": "launch",
"type": "f5anything",
"command": "hugo server --navigateToChanged --source ${workspaceFolder}/IdentityServer/v6/docs"
},
{
"name": "IdentityModel",
"request": "launch",
"type": "f5anything",
"command": "hugo server --navigateToChanged --source ${workspaceFolder}/IdentityModel"
}
]
}
6 changes: 6 additions & 0 deletions IdentityModel/archetypes/default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

15 changes: 15 additions & 0 deletions IdentityModel/cheatsheet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[See part 2]({{< ref "/quickstarts/2_interactive" >}})

[See part 2]({{< ref "2_interactive.md" >}})

{{< ref "2_interactive.md" >}}

[See part 1 - defining an API scope]({{< ref "1_client_credentials#defining-an-api-scope" >}})

{{< param qs_base >}}

{{% notice note %}}
...
{{% /notice %}}

![](../images/1_client_screenshot.png)
11 changes: 11 additions & 0 deletions IdentityModel/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
baseURL = "https://docs.duendesoftware.com/identitymodel"
languageCode = "en-us"
title = "IdentityModel Documentation"

theme = "hugo-theme-learn"
# For search functionality
[outputs]
home = [ "HTML", "RSS", "JSON"]

[params]
editURL = "https://github.com/DuendeSoftware/docs.duendesoftware.com/edit/main/IdentityModel/docs/content/"
51 changes: 51 additions & 0 deletions IdentityModel/content/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
+++
title = "Welcome to IdentityModel"
weight = 10
chapter = true
+++


Welcome to IdentityModel
========================

IdentityModel is a family of open source libraries for building OAuth 2.0 and OpenID
Connect clients.

## IdentityModel

The base library for OIDC and OAuth 2.0 related protocol operations. It provides an object
model to interact with the endpoints defined in the various OAuth and OpenId Connect
specifications in the form of types to represent the requests and responses, extension
methods to invoke requests constants defined in the specifications, such as standard
scope, claim, and parameter names, and other convenience methods for performing common
identity related operations

IdentityModel targets .NET Standard 2.0, making it suitable for .NET and .NET Framework.

- GitHub: <https://github.com/IdentityModel/IdentityModel>
- NuGet: <https://www.nuget.org/packages/IdentityModel/>
<!-- - CI builds <https://github.com/orgs/IdentityModel/packages> -->

The following libraries build on top of IdentityModel, and provide
specific implementations for different applications:

## IdentityModel.OidcClient

IdentityModel.OidcClient is an OpenID Connect (OIDC) client library for native
applications in .NET. It is a certified OIDC relying party and implements [RFC
8252](https://datatracker.ietf.org/doc/html/rfc8252/), "OAuth 2.0 for native
Applications". It provides types that describe OIDC requests and responses, low level
methods to construct protocol state and handle responses, and higher level methods for
logging in, logging out, retrieving userinfo, and refreshing tokens.

- GitHub: <https://github.com/IdentityModel/IdentityModel.OidcClient>
- NuGet: <https://www.nuget.org/packages/IdentityModel.OidcClient>
<!-- - CI builds <https://github.com/orgs/IdentityModel/packages> -->

## IdentityModel.AspNetCore.OAuth2Introspection

OAuth 2.0 token introspection authentication handler for ASP.NET Core.

- GitHub <https://github.com/IdentityModel/IdentityModel.AspNetCore.OAuth2Introspection>
- NuGet <https://www.nuget.org/packages/IdentityModel.AspNetCore.OAuth2Introspection/>
<!-- - CI builds <https://github.com/orgs/IdentityModel/packages> -->
100 changes: 100 additions & 0 deletions IdentityModel/content/endpoints/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
+++
title = "Protocol Endpoints"
weight = 10
chapter = true
+++

Overview
========

IdentityModel contains client libraries for many interactions with
endpoints defined in OpenID Connect and OAuth 2.0. All of these
libraries have a common design, let\'s examine the various layers using
the client for the token endpoint.

Request and response objects
----------------------------

All protocol request are modelled as request objects and have a common
base class called *ProtocolRequest* which has properties to set the
endpoint address, client ID, client secret, client assertion, and the
details of how client secrets are transmitted (e.g. authorization header
vs POST body). *ProtocolRequest* derives from *HttpRequestMessage* and
thus also allows setting custom headers etc.

The following code snippet creates a request for a client credentials
grant type:

```cs
var request = new ClientCredentialsTokenRequest
{
Address = "https://demo.identityserver.io/connect/token",
ClientId = "client",
ClientSecret = "secret"
};
```

While in theory you could now call *Prepare* (which internally sets the
headers, body and address) and send the request via a plain
*HttpClient*, typically there are more parameters with special semantics
and encoding required. That\'s why we provide extension methods to do
the low level work.

Equally, a protocol response has a corresponding *ProtocolResponse*
implementation that parses the status codes and response content. The
following code snippet would parse the raw HTTP response from a token
endpoint and turn it into a *TokenResponse* object:

```cs
var tokenResponse = await ProtocolResponse
.FromHttpResponseAsync<TokenResponse>(httpResponse);
```

Again these steps are automated using the extension methods. So let\'s
have a look at an example next.

Extension methods
-----------------

For each protocol interaction, an extension method for
*HttpMessageInvoker* (that's the base class of *HttpClient*) exists.
The extension methods expect a request object and return a response
object.

It is your responsibility to setup and manage the lifetime of the
*HttpClient*, e.g. manually:

```cs
var client = new HttpClient();

var response = await client.RequestClientCredentialsTokenAsync(
new ClientCredentialsTokenRequest
{
Address = "https://demo.identityserver.io/connect/token",
ClientId = "client",
ClientSecret = "secret"
});
```

You might want to use other techniques to obtain an *HttpClient*, e.g.
via the HTTP client factory:

```cs
var client = HttpClientFactory.CreateClient("my_named_token_client");

var response = await client.RequestClientCredentialsTokenAsync(
new ClientCredentialsTokenRequest
{
Address = "https://demo.identityserver.io/connect/token",
ClientId = "client",
ClientSecret = "secret"
});
```

All other endpoint client follow the same design.

{{% notice note %}}
Some client libraries also include a stateful client object (e.g.
*TokenClient* and *IntrospectionClient*). See the corresponding section
to find out more.
{{% /notice %}}
37 changes: 37 additions & 0 deletions IdentityModel/content/endpoints/device_authorize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
+++
title = "Device Authorization Endpoint"
weight = 70
+++

The client library for the [OAuth 2.0 device
flow](https://tools.ietf.org/html/rfc7662) device authorization is
provided as an extension method for *HttpClient*.

The following code sends a device authorization request:

```
var client = new HttpClient();

var response = await client.RequestDeviceAuthorizationAsync(new DeviceAuthorizationRequest
{
Address = "https://demo.identityserver.io/connect/device_authorize",
ClientId = "device"
});
```

The response is of type *DeviceAuthorizationResponse* and has properties
for the standard response parameters. You also have access to the the
raw response as well as to a parsed JSON document (via the *Raw* and
*Json* properties).

Before using the response, you should always check the *IsError*
property to make sure the request was successful:

```
if (response.IsError) throw new Exception(response.Error);

var userCode = response.UserCode;
var deviceCode = response.DeviceCode;
var verificationUrl = response.VerificationUri;
var verificationUrlComplete = response.VerificationUriComplete;
```
138 changes: 138 additions & 0 deletions IdentityModel/content/endpoints/discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
+++
title = "Discovery Endpoint"
weight = 20
+++

The client library for the [OpenID Connect discovery
endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html) is
provided as an extension method for *HttpClient*. The
*GetDiscoveryDocumentAsync* method returns a *DiscoveryResponse* object
that has both strong and weak typed accessors for the various elements
of the discovery document.

You should always check the *IsError* and *Error* properties before
accessing the contents of the document.

Example:

```
var client = new HttpClient();

var disco = await client.GetDiscoveryDocumentAsync("https://demo.identityserver.io");
if (disco.IsError) throw new Exception(disco.Error);
```

Standard elements can be accessed by using properties:

```
var tokenEndpoint = disco.TokenEndpoint;
var keys = disco.KeySet.Keys;
```

Custom elements (or elements not covered by the standard properties) can
be accessed like this:

```
// returns string or null
var stringValue = disco.TryGetString("some_string_element");

// return a nullable boolean
var boolValue = disco.TryGetBoolean("some_boolean_element");

// return array (maybe empty)
var arrayValue = disco.TryGetStringArray("some_array_element");

// returns JToken
var rawJson = disco.TryGetValue("some_element");
```

Discovery Policy \-\-\-\-\-\-\-\-\-\-\-\-\-\--By default the discovery
response is validated before it is returned to the client, validation
includes:

- enforce that HTTPS is used (except for localhost addresses)
- enforce that the issuer matches the authority
- enforce that the protocol endpoints are on the same DNS name as the
```
authority
```
- enforce the existence of a keyset

Policy violation errors will set the *ErrorType* property on the
*DiscoveryResponse* to *PolicyViolation*.

All of the standard validation rules can be modified using the
*DiscoveryPolicy* class, e.g. disabling the issuer name check:

```
var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = "https://demo.identityserver.io",
Policy =
{
ValidateIssuerName = false
}
});
```

You can also customize validation strategy based on the authority with
your own implementation of *IAuthorityValidationStrategy*. By default,
comparison uses ordinal string comparison. To switch to Uri comparison:

```
var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = "https://demo.identityserver.io",
Policy =
{
AuthorityValidationStrategy = new AuthorityUrlValidationStrategy()
}
});
```

Caching the Discovery Document
------------------------------

You should periodically update your local copy of the discovery
document, to be able to react to configuration changes on the server.
This is especially important for playing nice with automatic key
rotation.

The *DiscoveryCache* class can help you with that.

The following code will set-up the cache, retrieve the document the
first time it is needed, and then cache it for 24 hours:

```
var cache = new DiscoveryCache("https://demo.identityserver.io");
```

You can then access the document like this:

```
var disco = await cache.GetAsync();
if (disco.IsError) throw new Exception(disco.Error);
```

You can specify the cache duration using the *CacheDuration* property
and also specify a custom discovery policy by passing in a
*DiscoveryPolicy* to the constructor.

### Caching and HttpClient Instances

By default the discovery cache will create a new instance of
*HttpClient* every time it needs to access the discovery endpoint. You
can modify this behavior in two ways, either by passing in a pre-created
instance into the constructor, or by providing a function that will
return an *HttpClient* when needed.

The following code will setup the discovery cache in DI and will use the
*HttpClientFactory* to create clients:

```
services.AddSingleton<IDiscoveryCache>(r =>
{
var factory = r.GetRequiredService<IHttpClientFactory>();
return new DiscoveryCache(Constants.Authority, () => factory.CreateClient());
});
```
Loading
Loading