Skip to content

Commit 5130139

Browse files
authored
Merge pull request #21 from IdentityPython/config
Rewrote Client/RP configuration handling
2 parents d0466d4 + 060b83e commit 5130139

File tree

77 files changed

+2996
-1961
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+2996
-1961
lines changed

doc/client/config.rst

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
.. _config:
2+
3+
-----------------------
4+
RP/Client Configuration
5+
-----------------------
6+
7+
As you may have guessed by now a lot of the work you have to do to use this
8+
packages lies in the RP/Client configuration.
9+
I'll use just RP in the rest of the document but everywhere you see RP you
10+
can also think Client (in the OAuth2 sense).
11+
12+
The configuration parameters fall into 2 groups, one with basic information
13+
that are independent of which OpenID Provider (OP) that RP
14+
15+
General configuration parameters
16+
--------------------------------
17+
18+
Among the general parameters you have to define:
19+
20+
port
21+
Which port the RP is listening on
22+
23+
domain
24+
The domain the RP belongs to
25+
26+
these 2 together then defines the base_url. which is normally defined as::
27+
28+
base_url: "https://{domain}:{port}"
29+
30+
You can use the {domain} and {port} placeholders anywhere in the
31+
configuration. It will automatically be replaced with the domain and
32+
port values when the configuration is evaluated.
33+
34+
logging
35+
How the process should log
36+
37+
httpc_params
38+
Defines how the process performs HTTP requests to other entities.
39+
Parameters here are typically **verify** which controls whether the http
40+
client will verify the server TLS certificate or not.
41+
Other parameters are **client_cert**/**client_key** which are needed only
42+
if you expect the TLS server to ask for the clients TLS certificate.
43+
Something that happens if you run in an environment where mutual TLS is
44+
expected.
45+
46+
key_conf
47+
Definition of the private keys that all RPs are going to use in the OIDC
48+
protocol exchange.
49+
50+
There might be other parameters that you need dependent on which web framework
51+
you chose to use.
52+
53+
OP/AS specific configuration parameters
54+
---------------------------------------
55+
56+
The client configuration is keyed to an OP/AS name. This name should
57+
be something human readable it does not have to in anyway be linked to the
58+
issuer ID of the OP/AS.
59+
60+
The key **""** (the empty string) is chosen to represent all OP/ASs that
61+
are dynamically discovered.
62+
63+
Disregarding if doing everything dynamically or statically you **MAY**
64+
define which services the RP/Client should be able to use.
65+
If you don't the default set it used.
66+
The default set consists of the discovery, authorization, access_token and
67+
refresh_access_token services if we're talking OAuth2 and the discovery,
68+
registration, authorization, access_token, refresh_access_token and
69+
userinfo services if we are talking OIDC.
70+
71+
services
72+
A specification of the usable services which possible changes to the
73+
default configuration of those service.
74+
75+
Static configuration
76+
....................
77+
78+
If you have done manual client registration you will have to fill in these:
79+
80+
client_id
81+
The client identifier.
82+
83+
client_secret
84+
The client secret
85+
86+
redirect_uris
87+
A set of URLs from which the RP can chose one to be added to the
88+
authorization request. The expectation is that the OP/AS will redirect
89+
the use back to this URL after the authorization/authentication has
90+
completed. These URLs should be OP/AS specific.
91+
92+
behaviour
93+
Information about how the RP should behave towards the OP/AS. This is
94+
a set of attributes with values. The attributes taken from the
95+
`client metadata`_ specification. *behaviour* is used when the client
96+
has been registered statically and it is know what the client wants to
97+
use and the OP supports.
98+
99+
Usage example::
100+
101+
"behaviour": {
102+
"response_types": ["code"],
103+
"scope": ["openid", "profile", "email"],
104+
"token_endpoint_auth_method": ["client_secret_basic",
105+
'client_secret_post']
106+
}
107+
108+
109+
110+
Dynamic Configuration
111+
.....................
112+
113+
If the provider info discovery is done dynamically you need these
114+
115+
client_preferences
116+
How the RP should prefer to behave against the OP/AS. The content are the
117+
same as for *behaviour*. The difference is that this is specified if the
118+
RP is expected to do dynamic client registration which means that at the
119+
point of writing the configuration it is only known what the RP can and
120+
wants to do but unknown what the OP supports.
121+
122+
issuer
123+
The Issuer ID of the OP.
124+
125+
allow
126+
If there is a deviation from the standard as to how the OP/AS behaves this
127+
gives you the possibility to say you are OK with the deviation.
128+
Presently there is only one thing you can allow and that is the *issuer*
129+
in the provider info is not the same as the URL you used to fetch the
130+
information.
131+
132+
.. _client metadata: https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
133+
134+
-------------------------
135+
RP configuration - Google
136+
-------------------------
137+
138+
A working configuration where the client_id and client_secret is replaced
139+
with dummy values::
140+
141+
{
142+
"issuer": "https://accounts.google.com/",
143+
"client_id": "xxxxxxxxx.apps.googleusercontent.com",
144+
"client_secret": "2222222222",
145+
"redirect_uris": ["{}/authz_cb/google".format(BASEURL)],
146+
"behaviour": {
147+
"response_types": ["code"],
148+
"scope": ["openid", "profile", "email"],
149+
"token_endpoint_auth_method": ["client_secret_basic",
150+
'client_secret_post']
151+
},
152+
"services": {
153+
'ProviderInfoDiscovery': {},
154+
'Authorization': {},
155+
'AccessToken': {},
156+
'UserInfo': {}
157+
}
158+
}
159+
160+
161+
Now piece by piece
162+
163+
Information provided by Google::
164+
165+
"issuer": "https://accounts.google.com/",
166+
167+
Information about the client. When you register your RP with Google you will
168+
in return get a client_id and client_secret::
169+
170+
"client_id": "xxxxxxxxx.apps.googleusercontent.com",
171+
"client_secret": "2222222222",
172+
"redirect_uris": ["{}/authz_cb/google".format(BASEURL)],
173+
174+
Now to the behaviour of the client. Google specifies response_type *code* which
175+
is reflected here. The scopes are picked form the set of possible scopes that
176+
Google provides. And lastly the *token_endpoint_auth_method*, where Google
177+
right now supports 2 variants both listed here. The RP will by default pick
178+
the first if a list of possible values. Which in this case means the RP will
179+
authenticate using the *client_secret_basic* if allowed by Google::
180+
181+
"behaviour": {
182+
"response_types": ["code"],
183+
"scope": ["openid", "profile", "email"],
184+
"token_endpoint_auth_method": ["client_secret_basic",
185+
'client_secret_post']
186+
},
187+
188+
And lastly, which service the RP has access to. *ProviderInfoDiscovery* since
189+
Google supports dynamic provider info discovery. *Authorization* always must be
190+
there. *AccessToken* and *UserInfo* since response_type is *code* and Google
191+
return the user info at the userinfo endpoint::
192+
193+
194+
"services": {
195+
'ProviderInfoDiscovery': {},
196+
'Authorization': {},
197+
'AccessToken': {},
198+
'UserInfo': {}
199+
}
200+
201+
202+
----------------------------
203+
RP configuration - Microsoft
204+
----------------------------
205+
206+
Configuration that allows you to use a Microsoft OP as identity provider::
207+
208+
{
209+
'issuer': 'https://login.microsoftonline.com/<tenant_id>/v2.0',
210+
'client_id': '242424242424',
211+
'client_secret': 'ipipipippipipippi',
212+
"redirect_uris": ["{}/authz_cb/microsoft".format(BASEURL)],
213+
"behaviour": {
214+
"response_types": ["id_token"],
215+
"scope": ["openid"],
216+
"token_endpoint_auth_method": ['client_secret_post'],
217+
"response_mode": 'form_post'
218+
},
219+
"allow": {
220+
"issuer_mismatch": True
221+
},
222+
"services": {
223+
'ProviderInfoDiscovery':{},
224+
'Authorization': {}
225+
}
226+
}
227+
228+
One piece at the time. Microsoft has something called a tenant. Either you
229+
specify your RP to only one tenant in which case the issuer returned
230+
as *iss* in the id_token will be the same as the *issuer*. If our RP
231+
is expected to work in a multi-tenant environment then the *iss* will **never**
232+
match issuer. Let's assume our RP works in a single-tenant context::
233+
234+
'issuer': 'https://login.microsoftonline.com/<tenant_id>/v2.0',
235+
"allow": {
236+
"issuer_mismatch": True
237+
},
238+
239+
Information about the client. When you register your RP with Microsoft you will
240+
in return get a client_id and client_secret::
241+
242+
'client_id': '242424242424',
243+
'client_secret': 'ipipipippipipippi',
244+
"redirect_uris": ["{}/authz_cb/microsoft".format(BASEURL)],
245+
246+
Regarding the behaviour of the RP, Microsoft have chosen to only support the
247+
response_type *id_token*. Microsoft have also chosen to return the authorization
248+
response not in the fragment of the redirect URL which is the default but
249+
instead using the response_mode *form_post*. *client_secret_post* is a
250+
client authentication that Microsoft supports at the token enpoint::
251+
252+
"behaviour": {
253+
"response_types": ["id_token"],
254+
"scope": ["openid"],
255+
"token_endpoint_auth_method": ['client_secret_post'],
256+
"response_mode": 'form_post'
257+
},
258+
259+
And lastly, which service the RP has access to. *ProviderInfoDiscovery* since
260+
Microsoft supports dynamic provider info discovery. *Authorization* always must be
261+
there. And in this case this is it. All the user info will be included in the
262+
*id_token* that is returned in the authorization response::
263+
264+
"services": {
265+
'ProviderInfoDiscovery':{},
266+
'Authorization': {}
267+
}
268+
269+
270+
-------------------------
271+
RP configuration - GitHub
272+
-------------------------
273+
274+
As mentioned before GitHub runs an OAuth2 AS not an OP.
275+
Still we can talk to it using this configuration::
276+
277+
{
278+
"issuer": "https://github.com/login/oauth/authorize",
279+
'client_id': 'eeeeeeeee',
280+
'client_secret': 'aaaaaaaaaaaaa',
281+
"redirect_uris": ["{}/authz_cb/github".format(BASEURL)],
282+
"behaviour": {
283+
"response_types": ["code"],
284+
"scope": ["user", "public_repo"],
285+
"token_endpoint_auth_method": ['']
286+
},
287+
"provider_info": {
288+
"authorization_endpoint":
289+
"https://github.com/login/oauth/authorize",
290+
"token_endpoint":
291+
"https://github.com/login/oauth/access_token",
292+
"userinfo_endpoint":
293+
"https://api.github.com/user"
294+
},
295+
'services': {
296+
'Authorization': {},
297+
'AccessToken': {'response_body_type': 'urlencoded'},
298+
'UserInfo': {'default_authn_method': ''}
299+
}
300+
}
301+
302+
Part by part.
303+
Like with Google and Microsoft, GitHub expects you to register your client in
304+
advance. You register the redirect_uris and in return will get *client_id* and
305+
*client_secret*::
306+
307+
'client_id': 'eeeeeeeee',
308+
'client_secret': 'aaaaaaaaaaaaa',
309+
"redirect_uris": ["{}/authz_cb/github".format(BASEURL)],
310+
311+
Since GitHub doesn't support dynamic provder info discovery you have to enter
312+
that information in the configuration::
313+
314+
"issuer": "https://github.com/login/oauth/authorize",
315+
"provider_info": {
316+
"authorization_endpoint":
317+
"https://github.com/login/oauth/authorize",
318+
"token_endpoint":
319+
"https://github.com/login/oauth/access_token",
320+
"userinfo_endpoint":
321+
"https://api.github.com/user"
322+
},
323+
324+
Regarding the client behaviour the GitHub AS expects response_type *code*.
325+
The number of scope values is rather large I've just chose 2 here.
326+
No client authentication at the token endpoint is expected::
327+
328+
"behaviour": {
329+
"response_types": ["code"],
330+
"scope": ["user", "public_repo"],
331+
"token_endpoint_auth_method": ['']
332+
},
333+
334+
And about services, *Authorization* as always, *AccessToken* to convert the
335+
received *code* in the authorization response into an access token which later
336+
can be used to access user info at the userinfo endpoint.
337+
GitHub deviates from the standard in a number of way. First the Oauth2
338+
standard doesn't mention anything like an userinfo endpoint, that is OIDC.
339+
So GitHub has implemented something that is in between OAuth2 and OIDC.
340+
What's more disturbing is that the access token response by default is not
341+
encoded as a JSON document which the standard say but instead it's
342+
urlencoded. Lucky for us, we can deal with both these things by configuration
343+
rather then writing code.::
344+
345+
'services': {
346+
'Authorization': {},
347+
'AccessToken': {'response_body_type': 'urlencoded'},
348+
'UserInfo': {'default_authn_method': ''}
349+
}
350+

0 commit comments

Comments
 (0)