Integrating with Thinktecture Identity Server v3
My current project is looking to use OpenID Connect (oidc) and OAuth2 for authentication and authorization. We’ve decided to start out using the open source .Net implementation from Thinktecture. We’ve got to a point where we can authenticate and gain authorization from the identity server and call into a protected resource. This article is an overview of what we’ve done so far.
Firstly you’ll need to have the identity server running. I’m not covering how to get the identity server to connect to your own custom data store. There are various interfaces you can implement to do this, but for now we’re just working with the in memory users ‘bob’ and ‘alice’ that come as part of the identity server git repo. I opened the source in visual studio and started the host project which created an IISExpress site. I put some breakpoints in the code and stepped through to get a feel for what is going on though if you just want to have the server running it’s quite nice to spin up IISExpress with the site from the command line rather than have the project open in visual studio.
The next thing I did was create a website with a sign in button which redirects to the identity server logon page. At this point you’ll need to have some understanding of oidc and OAuth2. Here’s the url I redirect to:
Lets’ break this down and explain what it means. The authorize endpoint is the part of the url without the querystring. This endpoint performs the authorization and the querystring tells it what we want it to do. Now for the querystring parameters.
client_idis the id of the application requesting authorization. Clients must be registered with the identity server and if a
client_idwhich is not registered is received the request will be rejected.
scopevalue is a space separated list of values.
openidis part of the oidc spec and indicates that we want to authenticate the user. The profile scope means we want access to the user’s profile. The
idmgrscopes are resource scopes meaning that they are not part of the OAuth2/oidc spec but are defined by the resource (think api). Both these scopes are defined in identity server and I’ve used them in my demo projects but they are custom scopes and would typically relate to your api functionality.
redirect_urlis the url you want to be redirected to once authentication and/or authorization has taken place. The redirect url must be registered with identity server as part of the client and multiple urls can be specified. This feature means that malicious requests with evil redirect urls will be rejected.
stateparameter is a unique value which is generated and sent as part of the request. When the redirect occurs it will be passed back and the client should check that the response it receives contains the same
statevalue that it sent to the identity server. If it doesn’t it shouldn’t trust the response from the identity server.
response_typehere is ‘
id_token token’. The
id_tokenis part of oidc and is a feature which provides authentication in a way which OAuth2 does not. See section 10 (esp 10.16) in RFC6749. Essentially a valid
id_tokenis proof of authentication and not just of authorization to a user’s profile. The
tokenpart means that we’re requesting an access token rather than an authorization code (in which case the value would be
code). This means that we are initiating the OAuth2 implicit flow typically used by browser based clients. Server side clients would normally request an authorization code which would be sent to the browser and in turn to the server which would then exchange the code for an access token. The purpose of the authorization code is to prevent the access token itself from being sent to the browser where it can be easily read and potentially misused.
nonce(number used once) parameter will be encoded in the
id_token. When the
id_tokenis received back the client should check the
noncevalue it contains matches what was sent in a similar way to the
stateparameter. This enables the client to ensure it does not accept an
id_tokenwhich was not intended for it.
- Finally, the
form_postinstructs the identity server to cause the tokens to be received by the client as part of a form post.
The request we made to the identity server will result in two tokens being returned, an id token for authentication and an access token for authorization. It turns out that identity server can help us out with the access token as it has an access token validation endpoint which can accept the access token and return an unencrypted version which can be read with json.net in .Net 2.0. However identity server will not validate the id token for us, we must do that ourselves. So we created a webAPI project with a controller which can accept the signed id token, validate it and return the claims as json. This api is not public facing and will be deployed on the same server as the client to keep things private. Just to be clear, if you’re not working with a .Net 2.0 project you don’t need to have a separate service to validate the id token, so things are actually simpler.
So now we’re in a position where the client knows what claims have been authorized and it can ensure these are respected within the website. However it will also need to access the resource server which hosts some APIs we’ll be calling. We have the authorization token we need to pass to the resource server api so next we needed to make sure that the api was secured so that it would only allow access to authorized resources.
For this we copied the SampleAspNetWebApi from the Thinktecture samples. This is a basic web api project using Owin which introduces the Thinktecture.IdentityServer.v3.AccessTokenValidation authorization into the pipeline. This library takes the claims from the supplies access token and adds them to a
ClaimsIdentity which is set in an
AuthenticationTicket. This allows us to enforce authorization for controller actions decorated with the Authorize attribute.
In summary, this article has introduced the high level workflow involved in gaining access to an API secured with OAuth2 and OpenId Connect. Our specific scenario also introduced the need for a separate service to validate the id token though for the majority of applications this won’t be necessary as you’ll be able to take advantage of the .net 4.5 JWT libraries.
In the next article I’ll introduce the demo projects we created to implement the workflow described here.