Skip to content
This repository was archived by the owner on Jun 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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
225 changes: 5 additions & 220 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,232 +2,17 @@
Examples of a OIDC OPs with CherryPy, Flask and Django.
**NOT** something you should even image running in a production environment.


### Introduction

This project are here to show you how to 'build' an OP using the
classes and functions provided by oidc-op.

If you are just going to build a standard OP you only have to write the
configuration file. If you want to add or replace functionality this document
should be able to tell you how.

Setting up an OP means making a number if decisions. Like, should the OP support:

- [Web Finger](https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery)
- [dynamic discovery](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig)
- [dynamic client registration](https://openid.net/specs/openid-connect-registration-1_0.html)

All these are services you can access at endpoints. The total set of endpoints
that this package supports are

- webfinger
- provider_info
- registration
- authorization
- token
- refresh_token
- userinfo
- end_session


### Configuration directives


_issuer_

The issuer ID of the OP, unique value.

_capabilities_

This covers most of the basic functionality of the OP. The key words are the
same as defined in
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata .
A couple of things are defined else where. Like the endpoints, issuer id,
jwks_uri and the authentication methods at the token endpoint.

An example:

response_types_supported:
- code
- token
- id_token
- "code token"
- "code id_token"
- "id_token token"
- "code id_token token"
- none
response_modes_supported:
- query
- fragment
- form_post
subject_types_supported:
- public
- pairwise
grant_types_supported:
- authorization_code
- implicit
- urn:ietf:params:oauth:grant-type:jwt-bearer
- refresh_token
claim_types_supported:
- normal
- aggregated
- distributed

claims_parameter_supported: True
request_parameter_supported: True
request_uri_parameter_supported: True
frontchannel_logout_supported: True
frontchannel_logout_session_supported: True
backchannel_logout_supported: True
backchannel_logout_session_supported: True
check_session_iframe: https://127.0.0.1:5000/check_session_iframe


_id_token_

Defines which class that handles creating an ID Token and possibly also
arguments used when initiating that class.

An example:

id_token:
class: oidcendpoint.id_token.IDToken
kwargs:
default_claims:
email:
essential: True
email_verified:
essential: True


### OIDC Provider example setup

Create an environment
````
virtualenv -ppython3 env
source env/bin/activate
````

##### Install oidc-op
````
pip install git+https://github.com/rohe/oidc-op.git

# get the usage examples
git clone https://github.com/rohe/oidc-op.git
````

##### Configure a Django OP

See

https://github.com/peppelinux/django-oidc-op

##### Configure a Flask OP

````
pip install flask
cd oidc-op/

# configuration: create a private folder
cp -R flask_op/private .

# copy required files
cp flask_op/passwd.json private/
cp flask_op/conf.yaml private/
cp -R flask_op/templates .

# create a JWK for cookie signing
jwkgen --kty=SYM --kid cookie > private/cookie_sign_jwk.json
````

##### About JWK Set (JWKS) files
see: https://cryptojwt.readthedocs.io/en/latest/keyhandling.html

You can use `cryptojwt.key_jar.init_key_jar` to create JWKS file.
An easy way can be to configure the auto creation of JWKS files directly in your conf.yaml file.
Using `read_only: False` in `OIDC_KEYS` it will create the path within the JWKS files.
Change it to `True` if you don't want to overwrite them on each execution.

````
# in conf.yaml
#
OIDC_KEYS:
'private_path': './private/jwks.json'
'key_defs': *keydef
'public_path': './static/jwks.json'
# this will create the jwks files if they absent
'read_only': False
````

In the JWTConnect-Python-CryptoJWT distribution there is also a script you can use to construct a JWK.

You can for instance do:
````
$ jwkgen --kty=RSA
{
"d": "b9ucfay9vxDvz_nRZMVSUR9eRvHNMo0tc8Bl7tWkwxTis7LBXxmbMH1yzLs8omUil_u2a-Z_6VlKENxacuejYYcOhs6bfaU3iOqJbGi2p4t2i1oxjuF-cX6BZ5aHB5Wfb1uTXXobHokjcjVVDmBr_fNYBEPtZsVYqyN9sR9KE_ZLHEPks3IER09aX9G3wiB_PgcxQDRAl72qucsBz9_W9KS-TVWs-qCEqtXLmx9AAN6P8SjUcHAzEb0ZCJAYCkVu34wgNjxVaGyYN1qMA-1iOOVz--wtMyBwc5atSDBDgUApxFyj_DHSeBl81IHedcPjS9azxqFhumP7oJJyfecfSQ",
"e": "AQAB",
"kid": "cHZQbWRrMzRZak53U1pfSUNjY0dKd2xXaXRKenktdUduUjVBVTl3VE5ndw",
"kty": "RSA",
"n": "73XCXV2iiubSCEaFe26OpVnsBFlXwXh_yDCDyBqFgAFi5WdZTpRMJZoK0nn_vv2MvrXqFnw6IfXkwdsRGlMsNldVy36003gKa584CNksxfenwJZcF-huASUrSJEFr-3c0fMT_pLyAc7yf3rNCdRegzbBXSvIGKQpaeIjIFYftAPd9tjGA_SuYWVQDsSh3MeGbB4wt0lArAyFZ4f5o7SSxSDRCUF3ng3CB_QKUAaDHHgXrcNG_gPpgqQZjsDJ0VwMXjFKxQmskbH-dfsQ05znQsYn3pjcd_TEZ-Yu765_L5uxUrkEy_KnQXe1iqaQHcnfBWKXt18NAuBfgmKsv8gnxQ",
"p": "_RPgbiQcFu8Ekp-tC-Kschpag9iaLc9aDqrxE6GWuThEdExGngP_p1I7Qd7gXHHTMXLp1c4gH2cKx4AkfQyKny2RJGtV2onQButUU5r0gwnlqqycIA2Dc9JiH85PX2Z889TKJUlVETfYbezHbKhdsazjjsXCQ6p9JfkmgfBQOXM",
"q": "8jmgnadtwjMt96iOaoL51irPRXONO82tLM2AAZAK5Obsj23bZ9LFiw2Joh5oCSFdoUcRhbbIhCIv2aT4T_XKnDGnddrkxpF5Xgu0-hPNYnJx5m4kuzerot4j79Tx6qO-bshaaGz50MHs1vHSeFaDVN4fvh_hDWpV1BCNI0PKK-c"
}
SHA-256: pvPmdk34YjNwSZ_ICccGJwlWitJzy-uGnR5AU9wTNgw
````


##### Run the server
````
python -m flask_op.server private/conf.yaml
````

Then open your browser to `https://127.0.0.1:5000/.well-known/openid-configuration` to get the OpenID Provider Configuration resource.


##### Install OidcRP and configure flask-rp

It uses `JWTConnect-Python-OidcRP` as Relaing Party for tests, see [related page](https://github.com/openid/JWTConnect-Python-OidcRP).
You can run a working instance of `JWTConnect-Python-OidcRP.flask_rp` with:

````
pip install git+https://github.com/openid/JWTConnect-Python-OidcRP.git

# get entire project to have examples files
git clone https://github.com/openid/JWTConnect-Python-OidcRP.git
cd JWTConnect-Python-OidcRP

# run it as it come
python3 -m flask_rp.wsgi flask_rp/conf.yaml

# if you use django_op
RP_LOGFILE_NAME="./flrp.django.log" python3 -m flask_rp.wsgi django_op/example/data/oidc_rp/conf.django.yaml
````

Now you can connect to `https://127.0.0.1:8090/` to see the RP landing page and select your authentication endpoint.


### Authentication examples

![RP](doc/images/1.png)

Get to the RP landing page to choose your authentication endpoint. The first option aims to use _Provider Discovery_.

----------------------------------

![OP Auth](doc/images/2.png)

AS/OP accepted our authentication request and prompt to us the login form. Read passwd.json file to get credentials.

----------------------------------
configuration file. If you want to add or replace functionality please read the [Official Documentation](#TODO).

![Access](doc/images/3.png)

The identity representation with the information fetched from the user info endpoint.
# Contribute

----------------------------------
--

![Logout](doc/images/4.png)
# Authors

We can even test the single logout
- Roland Hedberg
76 changes: 66 additions & 10 deletions doc/source/contents/conf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
Configuration directives
========================

------
issuer
------

The issuer ID of the OP, a unique value in URI format.

------
add_on
------
Expand Down Expand Up @@ -49,7 +55,7 @@ An example::
"db": {
"class": "oidcop.util.JSONDictDB",
"kwargs": {
"json_path": "passwd.json"
"filename": "passwd.json"
}
},
"page_header": "Testing log in",
Expand All @@ -65,8 +71,7 @@ capabilities
------------

This covers most of the basic functionality of the OP. The key words are the
same as defined in
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata .
same as defined in `OIDC Discovery <https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata>`_.
A couple of things are defined else where. Like the endpoints, issuer id,
jwks_uri and the authentication methods at the token endpoint.

Expand Down Expand Up @@ -265,6 +270,9 @@ An example::
httpc_params
------------

Parameters submitted to the web client (python requests).
In this case the TLS certificate will not be verified, to be intended exclusively for development purposes

Example ::

"httpc_params": {
Expand All @@ -290,13 +298,6 @@ An example::
"essential": true
}}}},


------
issuer
------

The issuer ID of the OP.

----
keys
----
Expand Down Expand Up @@ -342,6 +343,41 @@ An example::
}
},

-----
authz
-----

This configuration section refers to the authorization/authentication endpoint behaviour.
Scopes bound to an access token are strictly related to grant management, as part of what that endpoint does.
Regarding grant authorization we should have something like the following example.

If you omit this section from the configuration (thus using some sort of default profile)
you'll have an Implicit grant authorization that leads granting nothing.
Add the below to your configuration and you'll see things changing.


An example::

"authz": {
"class": "oidcop.authz.AuthzHandling",
"kwargs": {
"grant_config": {
"usage_rules": {
"authorization_code": {
"supports_minting": ["access_token", "refresh_token", "id_token"],
"max_usage": 1
},
"access_token": {},
"refresh_token": {
"supports_minting": ["access_token", "refresh_token"]
}
},
"expires_in": 43200
}
}
},


-----------
session_key
-----------
Expand Down Expand Up @@ -430,3 +466,23 @@ An example::
}
}

This is somethig that can be customized.
For example in a django project we would use something like
the following (see `example/django_op/oidc_provider`):

"userinfo": {
"class": "oidc_provider.users.UserInfo",
"kwargs": {
# map claims to django user attributes here:
"claims_map": {
"phone_number": "telephone",
"family_name": "last_name",
"given_name": "first_name",
"email": "email",
"verified_email": "email",
"gender": "gender",
"birthdate": "get_oidc_birthdate",
"updated_at": "get_oidc_lastlogin"
}
}
}
5 changes: 4 additions & 1 deletion doc/source/contents/session_management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ item the user id. If you want the client session information the key is a
list with 2 items (user_id, client_id). And lastly if you want a grant then
the key is a list with 3 elements (user_id, client_id, grant_id).

Example::
"diana;;KtEST70jZx1x;;85544c9cace411ebab53559c5425fcc0"

A *session identifier* is constructed using the **session_key** function.
It takes as input the 3 elements list.::

Expand Down Expand Up @@ -158,7 +161,7 @@ might normally ask the user for usage consent and then base the construction
of the grant on that consent.

If an authorization server can act as a Security Token Service (STS) as
defined by https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16
defined by `Token Exchange [RFC-8693] <https://tools.ietf.org/html/rfc8693>`_
then no user is involved. In the context of session management the STS is
equivalent to a user.

Expand Down
Loading