Skip to content

Commit 190d152

Browse files
authored
Update user documentation (#121)
* Add text about new `token_auth`/`access_control` decorators. * Add info about Client Credentials Grant support.
1 parent 81c44be commit 190d152

File tree

7 files changed

+258
-101
lines changed

7 files changed

+258
-101
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77
This Flask extension provides simple OpenID Connect authentication, backed by [pyoidc](https://github.com/rohe/pyoidc).
88

9-
["Authorization Code Flow"](http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth), as well as
10-
["Implicit Flow"](https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth) and
11-
["Hybrid Flow"](https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth), is supported.
9+
["Authorization Code Flow"](http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth),
10+
["Implicit Flow"](https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth),
11+
["Hybrid Flow"](https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth),
12+
["Client Credentials Flow"](https://oauth.net/2/grant-types/client-credentials/) are supported.
1213

1314
## Getting started
1415
Read [the documentation](https://flask-pyoidc.readthedocs.io/) or have a look at the

docs/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,3 @@
5555
# relative to this directory. They are copied after the builtin static files,
5656
# so a file named "default.css" will overwrite the builtin "default.css".
5757
html_static_path = ['_static']
58-

docs/configuration.md

Lines changed: 78 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,93 @@
11
# Configuration
22

3-
## Provider and client configuration
3+
Both static and dynamic provider configuration discovery, as well as static and dynamic client registration, is
4+
supported. The different modes of provider configuration can be combined with any of the client registration modes.
45

5-
Both static and dynamic provider configuration discovery, as well as static
6-
and dynamic client registration, is supported. The different modes of provider configuration can be combined with any
7-
of the client registration modes.
6+
## Client Configuration
87

9-
### Provider configuration
8+
### Static Client Registration
109

11-
#### Dynamic provider configuration
10+
If you have already registered a client with the provider, specify the client credentials directly:
11+
```python
12+
from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata
13+
14+
client_metadata = ClientMetadata(client_id='client1', client_secret='secret1')
15+
```
16+
17+
**Note: The redirect URIs registered with the provider MUST include the URI specified in
18+
[`OIDC_REDIRECT_URI`](#flask-configuration).**
19+
20+
21+
### Dynamic Client Registration
22+
23+
To dynamically register a new client for your application, the required client registration info can be specified:
24+
25+
```python
26+
from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientRegistrationInfo
27+
28+
client_registration_info = ClientRegistrationInfo(client_name='Test App', contacts=['[email protected]'])
29+
```
30+
31+
## Provider configuration
32+
33+
### Dynamic provider configuration
1234

1335
To use a provider which supports dynamic discovery it suffices to specify the issuer URL:
1436
```python
1537
from flask_pyoidc.provider_configuration import ProviderConfiguration
1638

17-
config = ProviderConfiguration(issuer='https://op.example.com', [client configuration])
39+
# If you are using Static Client Configuration, then specify client_metadata
40+
# as shown above.
41+
provider_config = ProviderConfiguration(issuer='https://idp.example.com',
42+
client_metadata=client_metadata)
43+
44+
# If you are using Dynamic Client Registration, then specify
45+
# client_registration_info as shown above.
46+
provider_config = ProviderConfiguration(issuer='https://idp.example.com',
47+
client_registration_info=client_registration_info)
1848
```
1949

20-
#### Static provider configuration
50+
### Static provider configuration
2151

2252
To use a provider not supporting dynamic discovery, the static provider metadata can be specified:
2353
```python
2454
from flask_pyoidc.provider_configuration import ProviderConfiguration, ProviderMetadata
2555

26-
provider_metadata = ProviderMetadata(issuer='https://op.example.com',
27-
authorization_endpoint='https://op.example.com/auth',
28-
jwks_uri='https://op.example.com/jwks',
29-
userinfo_endpoint='https://op.example.com/userinfo')
30-
config = ProviderConfiguration(provider_metadata=provider_metadata, [client configuration])
56+
provider_metadata = ProviderMetadata(issuer='https://idp.example.com',
57+
authorization_endpoint='https://idp.example.com/auth',
58+
token_endpoint='https://idp.example.com/token',
59+
introspection_endpoint='https://idp.example.com/introspect',
60+
userinfo_endpoint='https://idp.example.com/userinfo',
61+
end_session_endpoint='https://idp.example.com/logout',
62+
jwks_uri='https://idp.example.com/certs',
63+
registration_endpoint='https://idp.example.com/registration'
64+
)
65+
# As shown earlier, if you are using Static Client Configuration, then specify
66+
# client_metadata.
67+
provider_config = ProviderConfiguration(provider_metadata=provider_metadata,
68+
client_metadata=client_metadata)
69+
70+
# If you are using Dynamic Client Registration, then specify
71+
# client_registration_info.
72+
provider_config = ProviderConfiguration(provider_metadata=provider_metadata,
73+
client_registration_info=client_registration_info)
3174
```
3275

3376
See the OpenID Connect specification for more information about the
3477
[provider metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata).
3578

36-
As mentioned in OpenID Connect specification, `userinfo_endpoint` is optional. If it's
37-
not provided, no userinfo request will be done and `flask_pyoidc.UserSession.userinfo` will be set to `None`.
79+
As mentioned in OpenID Connect specification, `userinfo_endpoint` is optional. If it's not provided, no userinfo
80+
request will be done and `flask_pyoidc.UserSession.userinfo` will be set to `None`.
3881

39-
#### Customizing authentication request parameters
82+
### Customizing authentication request parameters
4083
To customize the [authentication request parameters](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest),
4184
use `auth_request_params` in `ProviderConfiguration`:
4285
```python
4386
auth_params = {'scope': ['openid', 'profile']} # specify the scope to request
44-
config = ProviderConfiguration([provider/client config], auth_request_params=auth_params)
87+
provider_config = ProviderConfiguration([provider/client config], auth_request_params=auth_params)
4588
```
4689

47-
#### Session refresh
90+
### Session refresh
4891

4992
If your provider supports the `prompt=none` authentication request parameter, this extension can automatically refresh
5093
user sessions. This ensures that the user attributes (OIDC claims, user being active, etc.) are kept up-to-date without
@@ -53,43 +96,36 @@ refreshes:
5396
```python
5497
from flask_pyoidc.provider_configuration import ProviderConfiguration
5598

56-
config = ProviderConfiguration(session_refresh_interval_seconds=1800, [provider/client config])
99+
provier_config = ProviderConfiguration(session_refresh_interval_seconds=1800, [provider/client config])
57100
```
58101

59102
**Note: The user will still be logged out when the session expires (as set in the Flask session configuration).**
60103

61-
### Client configuration
62-
63-
#### Static client registration
104+
## Client Credentials Flow
105+
The [Client Credentials](https://tools.ietf.org/html/rfc6749#section-4.4) grant type is used by clients to obtain an access token outside of the context of a user.
64106

65-
If you have already registered a client with the provider, specify the client credentials directly:
66-
```python
67-
from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata
107+
This is typically used by clients to access resources about themselves rather than to access a user's resources.
68108

69-
client_metadata = ClientMetadata(client_id='cl41ekfb9j', client_secret='m1C659wLipXfUUR50jlZ')
70-
config = ProviderConfiguration([provider configuration], client_metadata=client_metadata)
71-
```
72-
73-
**Note: The redirect URIs registered with the provider MUST include the URI specified in
74-
[`OIDC_REDIRECT_URI`](#flask-configuration).**
75-
76-
#### Dynamic client registration
77-
78-
To dynamically register a new client for your application, the required client registration info can be specified:
109+
Client can obtain access token by using `client_credentials_grant`.
79110

80111
```python
81-
from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientRegistrationInfo
112+
auth = OIDCAuthentication({'default': provider_config}, app)
82113

83-
client_registration_info = ClientRegistrationInfo(client_name='Test App', contacts=['[email protected]'])
84-
config = ProviderConfiguration([provider configuration], client_registration_info=client_registration_info)
114+
client_credentials_response = auth.clients['default'].client_credentials_grant()
115+
access_token = resp.get('access_token')
85116
```
86117

118+
Use the obtained `access_token` to access your web service APIs.
119+
If your API endpoints are protected with `@auth.token_auth` or
120+
`@auth.access_control`, `access_token` will be verfied by token introspection
121+
before allowing access.
122+
87123
## Flask configuration
88124

89125
The application using this extension **MUST** set the following configuration parameters:
90126

91-
* `SECRET_KEY`: This extension relies on [Flask sessions](http://flask.pocoo.org/docs/quickstart/#sessions), which
92-
requires [`SECRET_KEY`](http://flask.pocoo.org/docs/config/#builtin-configuration-values).
127+
* `SECRET_KEY`: This extension relies on [Flask sessions](https://flask.palletsprojects.com/en/2.0.x/quickstart/#sessions), which
128+
requires [`SECRET_KEY`](https://flask.palletsprojects.com/en/2.0.x/config/#builtin-configuration-values).
93129
* `OIDC_REDIRECT_URI`: The URI used as redirect URI to receive authentication responses. This extension will add a url
94130
rule to handle all requests to the specified endpoint, so make sure the domain correctly points to your app and that
95131
the URL path is not already used in the app.
@@ -98,9 +134,9 @@ This extension also uses the following configuration parameters:
98134
* `OIDC_SESSION_PERMANENT`: If set to `True` (which is the default) the user session will be kept until the configured
99135
session lifetime (see below). If set to `False` the session will be deleted when the user closes the browser.
100136
* `PERMANENT_SESSION_LIFETIME`: Control how long a user session is valid, see
101-
[Flask documentation](http://flask.pocoo.org/docs/1.0/config/#PERMANENT_SESSION_LIFETIME) for more information.
137+
[Flask documentation](https://flask.palletsprojects.com/en/2.0.x/config/#PERMANENT_SESSION_LIFETIME) for more information.
102138

103-
#### Legacy configuration parameters
139+
### Legacy configuration parameters
104140
The following parameters have been deprecated:
105141
* `OIDC_REDIRECT_DOMAIN`: Set the domain (which may contain port number) used in the redirect_uri to receive
106142
authentication responses. Defaults to the `SERVER_NAME` configured for Flask.

docs/quickstart.md

Lines changed: 110 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# Quickstart
22

3-
To add authentication to one of your endpoints use the `oidc_auth` decorator:
43
```python
54
import flask
65
from flask import Flask, jsonify
@@ -14,20 +13,35 @@ app.config.update(
1413
OIDC_REDIRECT_URI = 'https://example.com/redirect_uri',
1514
SECRET_KEY = ...
1615
)
17-
config = ProviderConfiguration(...)
18-
auth = OIDCAuthentication({'default': config}, app)
1916

20-
@app.route('/login')
21-
@auth.oidc_auth('default')
22-
def index():
23-
user_session = UserSession(flask.session)
24-
return jsonify(access_token=user_session.access_token,
25-
id_token=user_session.id_token,
26-
userinfo=user_session.userinfo)
17+
# Static Client Registration
18+
# If you have already registered a client with the provider, specify the client
19+
# credentials directly:
20+
client_metadata = ClientMetadata(
21+
client_id='client1',
22+
client_secret='secret1',
23+
post_logout_redirect_uris=['https://example.com/logout'])
24+
25+
# Static provider configuration
26+
# To use a provider not supporting dynamic discovery, the static provider
27+
# metadata can be specified:
28+
provider_metadata = ProviderMetadata(
29+
issuer='https://idp.example.com',
30+
authorization_endpoint='https://idp.example.com/auth',
31+
token_endpoint='https://idp.example.com/token',
32+
introspection_endpoint='https://idp.example.com/introspect',
33+
userinfo_endpoint='https://idp.example.com/userinfo',
34+
end_session_endpoint='https://idp.example.com/logout',
35+
jwks_uri='https://idp.example.com/certs',
36+
registration_endpoint='https://idp.example.com/registration')
37+
38+
provider_config = ProviderConfiguration(provider_metadata=provider_metadata,
39+
client_metadata=client_metadata)
40+
41+
auth = OIDCAuthentication({'default': provider_config}, app)
2742
```
2843

29-
You can also use a Flask application factory:
30-
44+
You can also use Flask application factory:
3145
```python
3246
config = ProviderConfiguration(...)
3347
auth = OIDCAuthentication({'default': config})
@@ -42,6 +56,86 @@ def create_app():
4256
return app
4357
```
4458

59+
## OIDC
60+
61+
To add authentication to your endpoints use the `oidc_auth` decorator:
62+
```python
63+
@app.route('/api')
64+
@auth.oidc_auth('default')
65+
def index():
66+
user_session = UserSession(flask.session)
67+
return jsonify(access_token=user_session.access_token,
68+
id_token=user_session.id_token,
69+
userinfo=user_session.userinfo)
70+
```
71+
72+
## Token Based Authorization
73+
74+
To add token based authorization to your endpoints use the `token_auth`
75+
decorator. It allows you to call your endpoint with curl and REST API clients
76+
given "Authorization Bearer <access_token>" field is present in the request
77+
header. Token based authorization is backed by token introspection so ensure
78+
that Identity Provider's introspection endpoint is provided.
79+
```python
80+
provider_metadata = ProviderMetadata(
81+
...,
82+
introspection_endpoint='https://idp.example.com/introspect',
83+
...)
84+
85+
@app.route('/api')
86+
@auth.token_auth('default')
87+
def index():
88+
current_token_identity = auth.current_token_identity
89+
...
90+
91+
# Optionally, you can specify scopes required by your endpoint.
92+
@app.route('/api')
93+
@auth.token_auth('default',
94+
scopes_required=['read', 'write'])
95+
def index():
96+
current_token_identity = auth.current_token_identity
97+
...
98+
```
99+
To obtain information about the token, use `auth.current_token_identity` inside
100+
your endpoint. `current_token_identity` persists for current request only.
101+
102+
## OIDC & Token Based Authorization
103+
104+
If you want to apply both oidc based authentication and token based
105+
authorization on your endpoint, you can use `access_control` decorator.
106+
```python
107+
# If you are using Static Provider Configuration, add introspection_endpoint
108+
# in ProviderMetadata.
109+
provider_metadata = ProviderMetadata(
110+
...,
111+
introspection_endpoint='https://idp.example.com/introspect',
112+
...)
113+
114+
@app.route('/api')
115+
@auth.access_control('default')
116+
def index():
117+
current_identity = None
118+
if auth.current_token_identity:
119+
current_identity = auth.current_token_identity
120+
else:
121+
current_identity = UserSession(flask.session)
122+
...
123+
124+
# Optionally, you can specify scopes required by your endpoint.
125+
@app.route('/api')
126+
@auth.access_control('default',
127+
scopes_required=['read', 'write'])
128+
def index():
129+
current_identity = None
130+
if auth.current_token_identity:
131+
current_identity = auth.current_token_identity
132+
else:
133+
current_identity = UserSession(flask.session)
134+
...
135+
```
136+
137+
---
138+
45139
After a successful login, this extension will place three things in the user session (if they are received from the
46140
provider):
47141
* [ID Token](http://openid.net/specs/openid-connect-core-1_0.html#IDToken)
@@ -56,25 +150,17 @@ In addition to this documentation, you may have a look on a
56150
To allow users to login with multiple different providers, configure all of them in the `OIDCAuthentication`
57151
constructor and specify which one to use by name for each endpoint:
58152
```python
59-
from flask_pyoidc import OIDCAuthentication
60-
from flask_pyoidc.provider_configuration import ProviderConfiguration
61-
62-
app = Flask(__name__)
63-
app.config.update(
64-
OIDC_REDIRECT_URI = 'https://example.com/redirect_uri',
65-
SECRET_KEY = ...
66-
)
67153
auth = OIDCAuthentication({'provider1': ProviderConfiguration(...), 'provider2': ProviderConfiguration(...)}, app)
68154

69155
@app.route('/login1')
70156
@auth.oidc_auth('provider1')
71157
def login1():
72-
pass
158+
...
73159

74160
@app.route('/login2')
75161
@auth.oidc_auth('provider2')
76162
def login2():
77-
pass
163+
...
78164
```
79165

80166
## User logout
@@ -84,11 +170,11 @@ To support user logout, use the `oidc_logout` decorator:
84170
@app.route('/logout')
85171
@auth.oidc_logout
86172
def logout():
87-
return 'You\'ve been successfully logged out!'
173+
return "You've been successfully logged out!"
88174
```
89175

90176
If the logout view is mounted under a custom endpoint (other than the default, which is
91-
[the name of the view function](http://flask.pocoo.org/docs/1.0/api/#flask.Flask.route)), or if using Blueprints, you
177+
[the name of the view function](https://flask.palletsprojects.com/en/2.0.x/api/#flask.Flask.route)), or if using Blueprints, you
92178
must specify the full URL in the Flask-pyoidc configuration using `post_logout_redirect_uris`:
93179
```python
94180
ClientMetadata(..., post_logout_redirect_uris=['https://example.com/post_logout']) # if using static client registration

0 commit comments

Comments
 (0)