| ... | @@ -3,8 +3,22 @@ OpenID exists so that individual sites do not need to implement ad-hoc login sys |
... | @@ -3,8 +3,22 @@ OpenID exists so that individual sites do not need to implement ad-hoc login sys |
|
|
|
|
|
|
|
[DjangoLDP-Account](https://git.startinblox.com/djangoldp-packages/djangoldp-account) provides an implementation which plays both roles- the provider and the relying party. It enables federated user authentication, providing a single identity which can be used across various RPs and client-side applications
|
|
[DjangoLDP-Account](https://git.startinblox.com/djangoldp-packages/djangoldp-account) provides an implementation which plays both roles- the provider and the relying party. It enables federated user authentication, providing a single identity which can be used across various RPs and client-side applications
|
|
|
|
|
|
|
|
|
# The Federated User
|
|
|
|
|
|
|
|
A federated user is a user which can authenticate with multiple servers. This means that you can login to `aliceswebsite.com` as a user from `bobswebsite.com`. For an introduction to linked data, please see [the W3C's primer](https://www.w3.org/TR/ldp-primer/)
|
|
|
|
|
|
|
|
DjangoLDP achieves this using [OpenID Connect (OIDC)](https://openid.net/connect/). In OIDC users are globally identifiable via a "WebFinger ID". An example is `alice@aliceswebsite.com`, where `alice` is the username, hosted by the provider `aliceswebsite.com`. Using OIDC Alice can authenticate with `aliceswebsite.com`, authorising `bobswebsite.com` to make use of her account
|
|
|
|
|
|
|
|
When implementing authentication in your own application, you have two options:
|
|
|
|
* Using or extending DjangoLDP-Account, a DjangoLDP package modelling federated users
|
|
|
|
* Using your own user model & defining the authentication behaviour yourself
|
|
|
|
|
|
|
|
Regardless of the choice you make, DjangoLDP will extend your user model to include a method `webid` which returns uri to identify your user, for example `https://aliceswebsite.com/users/alice`. This is used in `LDPSerializer` so that views are returned in linked-data format, describing the semantics and location of resources in the response
|
|
|
|
|
|
|
# Using DjangoLDP-Account's Authentication
|
|
# Using DjangoLDP-Account's Authentication
|
|
|
|
|
|
|
|
|
It's recommended that you use or extend DjangoLDP-Account, as out of the box it provides all you need to have federated users which can log in from multiple sources, implementing both the endpoints for an OIDC provider and the Relying Party
|
|
|
|
|
|
|
If you want to use DjangoLDP-Account, but you don't need to understand its internal workings, please refer to the [DjangoLDP-Account Readme](https://git.startinblox.com/djangoldp-packages/djangoldp-account) which has a step-by-step on installation and usage
|
|
If you want to use DjangoLDP-Account, but you don't need to understand its internal workings, please refer to the [DjangoLDP-Account Readme](https://git.startinblox.com/djangoldp-packages/djangoldp-account) which has a step-by-step on installation and usage
|
|
|
|
|
|
|
|
# How It Works
|
|
# How It Works
|
| ... | @@ -42,3 +56,37 @@ The key function is `op_login_url`, which resolves the URL with which to make th |
... | @@ -42,3 +56,37 @@ The key function is `op_login_url`, which resolves the URL with which to make th |
|
|
During issuer discovery, the RP uses the host from the webID (e.g. `example.com` in the webID `alice@example.com`) and queries it with the resource `https://example.com/.well-known/webfinger?resource=acct%3Aalice%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer`
|
|
During issuer discovery, the RP uses the host from the webID (e.g. `example.com` in the webID `alice@example.com`) and queries it with the resource `https://example.com/.well-known/webfinger?resource=acct%3Aalice%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer`
|
|
|
|
|
|
|
|
DjangoLDP provides this functionality leveraging an external library, [django-oidc-provider](https://django-oidc-provider.readthedocs.io/en/latest/). It defines a `WebFingerEndpoint` which can be found [here](https://git.startinblox.com/djangoldp-packages/djangoldp/blob/master/djangoldp/endpoints/webfinger.py), and a `WebFinger` class which DjangoLDP-Account [builds on](https://git.startinblox.com/djangoldp-packages/djangoldp-account/blob/master/djangoldp_account/endpoints/webfinger.py)
|
|
DjangoLDP provides this functionality leveraging an external library, [django-oidc-provider](https://django-oidc-provider.readthedocs.io/en/latest/). It defines a `WebFingerEndpoint` which can be found [here](https://git.startinblox.com/djangoldp-packages/djangoldp/blob/master/djangoldp/endpoints/webfinger.py), and a `WebFinger` class which DjangoLDP-Account [builds on](https://git.startinblox.com/djangoldp-packages/djangoldp-account/blob/master/djangoldp_account/endpoints/webfinger.py)
|
|
|
|
|
|
|
|
# Using Your Own User Model
|
|
|
|
|
|
|
|
It's not required to use or extend the `LDPUser` user model from DjangoLDP-Account, although doing so is the recommended option because DjangoLDP-Account defines the behaviour of an OIDC Provider & Relying Party for you
|
|
|
|
|
|
|
|
If you can't use this class or don't want to then you don't have to, provided you meet the following requirements:
|
|
|
|
|
|
|
|
## Preparing the User Model
|
|
|
|
|
|
|
|
your user model must extend `DjangoLDP.Model`, or defines a `urlid` field on the user model, for example:
|
|
|
|
```
|
|
|
|
urlid = LDPUrlField(blank=True, null=True, unique=True)
|
|
|
|
```
|
|
|
|
|
|
|
|
**Why?**
|
|
|
|
|
|
|
|
`LDPUrlField` is based on Django's models.URLField and is defined [here](https://git.startinblox.com/djangoldp-packages/djangoldp/blob/master/djangoldp/fields.py)
|
|
|
|
The `urlid` is an identifier for the resource, for example `https://aliceswebsite.com/users/alice/`. It can be generated at runtime for a local user (we could build the example from the username `alice` and the users endpoint), but when a user from another site, `https://bobswebsite.com/users/bob/` logs into the server, a local copy will be created, which needs to store bob's `urlid`
|
|
|
|
|
|
|
|
## Implementing the OIDC Provider (OP)
|
|
|
|
|
|
|
|
If you would like the users of your site to be able to log in to other sites using their account, you will need to implement an OIDC Provider
|
|
|
|
|
|
|
|
This can be achieved by extending the [webfinger endpoint in DjangoLDP](https://git.startinblox.com/djangoldp-packages/djangoldp/blob/master/djangoldp/endpoints/webfinger.py). An example implementation [can be found in DjangoLDP-Account](https://git.startinblox.com/djangoldp-packages/djangoldp-account/blob/master/djangoldp_account/endpoints/webfinger.py)
|
|
|
|
|
|
|
|
OIDC issuer discovery is achieved on the url `.wellknown/webfinger/`, passing the Webfinger ID of the user. The provider returns the information required by the Relying Party, for example DjangoLDP-Account extends the returning of information on the issuer for the account
|
|
|
|
|
|
|
|
You can subclass `WebFinger`, overriding the `response` method to add information to the response for a given user. The `response_dict` parameter may already contain information before you return it, as DjangoLDP automatically passes it to each subclass of `WebFinger` before returning it from the view
|
|
|
|
|
|
|
|
More is happening under the surface, as DjangoLDP comes packaged with [django-oidc-provider](https://django-oidc-provider.readthedocs.io/en/latest/) which handles much of the workflow
|
|
|
|
|
|
|
|
* If you would like users from other sites to log into your site, then you will need to implement the endpoints of the Relying Party (RP)
|
|
|
|
|
|
|
|
An example implementation of the Relying Party endpoint [can be found in DjangoLDP-Account](https://git.startinblox.com/djangoldp-packages/djangoldp-account/blob/master/djangoldp_account/endpoints/rp_login.py). When the user submits their webfinger ID or OIDC provider to the [login form](https://git.startinblox.com/djangoldp-packages/djangoldp-account/blob/master/djangoldp_account/templates/registration/login.html), the endpoint utilises [PyOIDC](https://pyoidc.readthedocs.io/en/latest/examples/rp.html) to discover the issuer of the provider, and provides the callback function in this endpoint to log the user in once authenticated |
|
|
|
\ No newline at end of file |