Version 14.1 by messines on 2022/08/05 10:32

Show last authors
1 == Requirement ==
2
3 You should read this documentation to [[understand the concept of Authentication and Authorization>>doc:Collabs.the-collaboratory.Documentation IAM.FAQ.OIDC Clients explained.WebHome]] with OIDC and OAuth2 before trying to implement it.
4
5 == Abstract ==
6
7 In order to create an OIDC client, see [[1. Registering an OIDC client>>doc:Collabs.the-collaboratory.Documentation IAM.FAQ.OIDC Clients explained.1\. Registering an OIDC client v2.WebHome]]. After creating the OIDC client, you have a corresponding access token and secret.
8
9 For the example below, we consider the case of someone wanting to provide access to https:~/~/www.getpostman.com as an app for Collaboratory users to access from their collabs. You should replace that URL by the one of your own app.
10
11 The redirect_uri is set with the URL of your application to which your users will be redirected after having been authenticated by their EBRAINS account. For example when you login to this wiki, the redirect URI is [[https:~~/~~/wiki.ebrains.eu/*>>url:https://wiki.ebrains.eu/*]]
12
13 [[image:https://wiki.ebrains.eu/bin/download/Collabs/collaboratory-community-apps/Community%20App%20Developer%20Guide/Authenticating%20with%20your%20OIDC%20client%20and%20fetch%20collab%20user%20info/WebHome/Screenshot%202020-07-15%20at%2017.47.12.png?width=758&height=517&rev=1.1||alt="Screenshot 2020-07-15 at 17.47.12.png" height="517" width="758"]]
14
15 The whole authentication flow presented here is based on the official OAuth2 RFC described in the section 4.1.
16
17 [[https:~~/~~/tools.ietf.org/html/rfc6749#section-4.1>>url:https://tools.ietf.org/html/rfc6749#section-4.1]]
18
19 [[image:https://wiki.ebrains.eu/bin/download/Collabs/collaboratory-community-apps/Community%20App%20Developer%20Guide/Authenticating%20with%20your%20OIDC%20client%20and%20fetch%20collab%20user%20info/WebHome/Screenshot%202020-07-15%20at%2018.32.14.png?width=474&height=410&rev=1.2||alt="Screenshot 2020-07-15 at 18.32.14.png" height="410" width="474"]]
20
21 == Authentication flow ==
22
23 === Authorization Code Request ===
24
25 The first step of the authentication protocol is to fetch an **authorization code **for your client and your user. This is done by directing your users to the URL of the EBRAINS login page (**IAM**) where they can enter their username and password.
26
27 ==== Request ====
28
29 The authorization **code **is fetched by an HTTP request:
30
31 /GET: [[https:~~/~~/iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/auth>>url:https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/auth]]
32
33 with the following parameters:
34
35 * response_type=code
36 * login=true
37 * client_id=**//community-apps-tutorial//**
38 * redirect_uri=**//https:~/~/www.getpostman.com/oauth2/callback//**
39 * scope=openid**//+group+team//**
40
41 with the italics indicating the fields you customize for your own app. The URL will look like:
42
43 https:~/~/iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/auth?response_type=code&login=true&client_id=//**community-apps-tutorial**//&redirect_uri=//**https:~/~/www.getpostman.com/oauth2/callback**//&scope=openid//**+group+team**//
44
45 The **scope** parameter can include a combination of several values. Each user will be asked to consent to sharing that scope with your app upon first access.
46
47 * **openid: **This scope is required because we use the OIDC protocol. It will give your app access to the user's basic information such as username, email and full name.
48 * **profile** (optional): More information on user if provided by the user
49 * **email **(optional): The verified email of the user, should be add in addition of openid and/or profile to get the email.
50 * **group **(optional)**: **If you request this scope, the future access token generated will authorize your app to identify which units and groups the user belongs to.
51 * **team **(optional)**: **This scope is like the group scope lets your app identify the permissions of the user, but by identifying what collabs the user has access to and with what roles.
52 * **clb.wiki.read **(optional): access to GET Collab API
53 * **clb.wiki.write** (optional): access to DELETE/PUT/POST Collab API
54 * **collab.drive **(optional): access to GET/POST/PUT/DELETE drive API
55 * **offline_access **(optional)**: **provide refresh token
56 * **quota : **User's quota usage around EBRAINS services
57
58 The group and team scopes are a simple way for your app to grant permissions to its services and resources when you want to grant access to a very few units, groups, or collab teams. For more complex permission management, contact support.
59
60 ==== Response ====
61
62 Once the user has logged in, your app gets an HTTP 301 redirection followed by an HTTP 200 success response with an authorization **code** inside. A typical response might look like:
63
64 https:~/~/www.getpostman.com/oauth2/callback?session_state=a0ff8a68-2654-43ef-977a-6c15ce343546&code=**f3f04f93-hbp-482d-ac3d-demo.turtorial.7122c1d9-3f7e-4d80-9c4f-dcd244bc2ec7**
65
66 The authorization **code** is the part in bold in the response above.
67
68 === Access Token Request ===
69
70 Now that your app has the **authorization** **code** for a user, it can fetch the user ID Token and Access Token
71
72 ==== Request ====
73
74 /POST: [[https:~~/~~/iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/token>>url:https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/token]]
75
76 with the following parameters:
77
78 * grant_type: authorization_code
79 * code: **//f3f04f93-hbp-482d-ac3d-demo.turtorial.7122c1d9-3f7e-4d80-9c4f-dcd244bc2ec7//**
80 * redirect_uri: **//[[https:~~/~~/www.getpostman.com/oauth2/callback>>url:https://www.getpostman.com/oauth2/callback]]//**
81 * client_id: **//community-apps-tutorial//**
82 * client_secret: **//your client secret obtained during client creation//**
83
84 The image below shows a sample POST request generated from the Postman tool. [The fact that this page is based on getpostman.com as an example is pure coincidence.]
85
86 [[image:https://wiki.ebrains.eu/bin/download/Collabs/collaboratory-community-apps/Community%20App%20Developer%20Guide/Authenticating%20with%20your%20OIDC%20client%20and%20fetch%20collab%20user%20info/WebHome/Screenshot%202020-07-15%20at%2018.20.34.png?rev=1.1||alt="Screenshot 2020-07-15 at 18.20.34.png"]]
87
88 ==== Response ====
89
90 200 OK
91
92 {
93 "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAi...pP5vaNwvvsaNGEA",
94 "expires_in": 604773,
95 "refresh_expires_in": 604773,
96 "refresh_token": "eyJh...vC5eIR1rNhRJ4d8",
97 "token_type": "bearer",
98 "id_token": "eyJ...YOwdQ",
99 "not-before-policy": 0,
100 "session_state": "76e553bf-ba2e-45b6-8c6c-c867772b40ec",
101 "scope": "openid"
102 }
103
104 Your app gets a response containing the **access token**, the **refresh token,** the **id token **and other information. The ID Token should be use by developer on their backend to read user informations such as username, first name, last name etc. The ID Token should be use internally, into your app only, the app which triggered the authentication. The access token will be use to reach APIs, the access token can be see as a card to access an ATM. ID Token is for Authentication, Access token is for Authorization. Refresh token is to re-ask a valid access token after expiration.
105
106 == Access user info ==
107
108 Now that your app has the access token of a user, it can fetch the user's info.
109
110 ==== Request ====
111
112 /GET: [[https:~~/~~/iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/userinfo>>url:https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/userinfo]]
113
114 with the following parameters:
115
116 * Authorization: the access token preceded by the string "Bearer "
117
118 The image below shows a sample GET request generated from the Postman tool. [The fact that this page is based on getpostman.com as an example is pure coincidence.]
119
120 [[image:https://wiki.ebrains.eu/bin/download/Collabs/collaboratory-community-apps/Community%20App%20Developer%20Guide/Authenticating%20with%20your%20OIDC%20client%20and%20fetch%20collab%20user%20info/WebHome/Screenshot%202020-07-15%20at%2018.28.28.png?width=566&height=161&rev=1.1||alt="Screenshot 2020-07-15 at 18.28.28.png" height="161" width="566"]]
121
122 ==== Response ====
123
124 As response your app receives a JSON with all the information about the logged user
125
126 {
127 (% style="color:#2980b9" %)"sub": "fa2db206-3...0ebaba98e1",
128
129 (% style="color:#27ae60" %)"**preferred_username**": "demoaccount",(%%)
130 "unit": [
131 "/all/institutions/switzerland/epfl",
132 "/all/projects/hbp/consortium/SGA2/SP05",
133 "/all/projects/hbp/consortium/SGA3/WP6/T6_11"
134 ],
135 "roles": {
136 "jupyterhub": [
137 "feature:authenticate"
138 ],
139 "xwiki": [
140 "feature:authenticate"
141 ],
142 "team": [
143 "**collab**-collaboratory-community-apps-**editor**"
144 ],
145 "group": [
146 "**group**-collaboratory-developers",
147 "**unit**-all-projects-hbp-consortium-sga2-sp05-**administrator**"
148 ]
149 },
150 "mitreid-sub": "30...62"
151 }
152
153 The unit field above lists Collaboratory Units which the user is a member of, with the unit name using slashes instead of the colons you see in the Collaboratory UI.
154
155 jupyterhub and xwiki are OIDC clients with more advanced permission management.
156
157 The team field above lists Collaboratory Teams which the user is a member of, in the form "collab-//collabname//-//role//" where //role //is one of admin, editor, or viewer according to the user's role in collab //collabname//.
158
159 The group field above lists Collaboratory Groups which the user is a member of, in the form "group-//groupname//". It also lists Collaboratory Units which the user is an admin of, in the form "unit-//unitname//-administrator" with //unitname //using dashes instead of the colons you see in the Collaboratory UI.
160
161 ====
162 Which unique identifier should you use for your users ====
163
164 In Collaboratory 2, we recommand you to use the "(% style="color:#27ae60" %)preferred_username(%%)" field (which is the username) to identify your users for every new users in your system. The "(% style="color:#27ae60" %)preferred_username(%%)*" is used by us as unique identifier in The Collaboratory, it's really friendly to use it and interact easily with our IDM api. We are guaranting its unicity.
165
166 You can also use the "(% style="color:#2980b9" %)sub(%%)" provided by keycloak which is the OIDC/OAuth2 standard, its unicity is also guarantee by the system but it's specific to our Identity Providers Keycloak and not really user friendly.
167
168 In order to map Collaboratory 1 userids which you might have stored, you can use the "~*~*mitreid-sub~*~*" field, which is the Collaboratory 1 userid for the user with the same username / email address at the time of the registration of the Collaboratory 2 account. In the exceptional cases when the access token does not provide a "mitreid-sub" value, you can consider that the user doesn't have an account in Collaboratory 1. After shutdown of the collaboratory one, new users not havening a Collaboratory 1 account will be the norm obviously because Collaboratory 1 won't exist anymore. At this moment we are recommanding you to still use `mitreid-sub` when available and to consider using `preferred_username` or the new keycloak `sub` (not really user friendly) for your new users.
169
170 == OIDC Plugin configuration exemple ==
171
172 You will probably not reinvent the wheel and use an existing OIDC Library to establish the authentication. Often theses libary do all the above flow for you, you just have to fill a properties file.
173
174 === OIDC Plugin Properties : Usefull link ===
175
176 All the link you need to know to fill the property file of your OIDC plugin are visibles here
177
178 [[https:~~/~~/iam.ebrains.eu/auth/realms/hbp/.well-known/openid-configuration>>https://iam.ebrains.eu/auth/realms/hbp/.well-known/openid-configuration]]
179
180 === OIDC Plugin for Python : mozilla-django-oidc ===
181
182 We are using this plugin with our Django based project, a full documentation is available here
183 [[https:~~/~~/mozilla-django-oidc.readthedocs.io/en/stable/installation.html>>https://mozilla-django-oidc.readthedocs.io/en/stable/installation.html]]
184
185
186 ==== Github demo project using mozilla-django-oidc
187 [[https:~~/~~/github.com/HumanBrainProject/mozilla-django-oidc-demo>>https://github.com/HumanBrainProject/mozilla-django-oidc-demo]] ====
188
189 ==== ====
190
191 You can found a very basic empty Django project using mozilla-django-oidc on this github. Clone it and follow the README instruction. In few minutes if you already have a clientId and clientSecret available you should be able to login in this demo application. Once you validated that your client works well, you can compare with your own code and debug your own code to made the login works.
192
193
194 In the future, I will add some branch in this project with advanced usage exemple as overriding default mozilla-django-oidc class and fetch team and groups/units of a user.
195 \\For now, you can found more advanced usage in the CodeJam#12 presentation
196 \\[[https:~~/~~/drive.ebrains.eu/lib/cf5b0375-4e07-4e62-847d-e2997eac3f30/file/CodeJam%20%2312%20(2021)/Presentations/Oidc_integration_for_python.pptx>>https://drive.ebrains.eu/lib/cf5b0375-4e07-4e62-847d-e2997eac3f30/file/CodeJam%20%2312%20(2021)/Presentations/Oidc_integration_for_python.pptx]]
197
198 You can see every function of the original mozilla-django-oidc on their github, so you can better understand what you can override and how
199 \\[[https:~~/~~/github.com/mozilla/mozilla-django-oidc/blob/master/mozilla_django_oidc/auth.py>>https://github.com/mozilla/mozilla-django-oidc/blob/master/mozilla_django_oidc/auth.py]]
200
201
202 ==== mozilla-django-oidc sample config ====
203
204 {{code}}
205 # Mozilla django oidc settings
206 OIDC_OP_AUTHORIZATION_ENDPOINT = "https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/auth"
207 OIDC_OP_TOKEN_ENDPOINT = "https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/token"
208 OIDC_OP_USER_ENDPOINT = "https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/userinfo"
209 OIDC_RP_SIGN_ALGO ="RS256"
210 OIDC_OP_JWKS_ENDPOINT="https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/certs"
211 LOGIN_REDIRECT_URL = '/'
212 OIDC_STORE_ACCESS_TOKEN = True
213
214 # Store secret somewhere on your system
215 OIDC_RP_SCOPES="openid profile email"
216 OIDC_RP_CLIENT_ID = os.environ['OIDC_RP_CLIENT_ID']
217 OIDC_RP_CLIENT_SECRET = os.environ['OIDC_RP_CLIENT_SECRET']
218
219 # Theses one are situational, you problably don't need them, it's to use in the case you want to override an action in a python class, for exemple execute some code after the callback
220
221 OIDC_CALLBACK_CLASS = 'clb_auth.views.OIDCAuthenticationCallbackView'
222 OIDC_DRF_AUTH_BACKEND = 'clb_auth.auth.OIDCBearerAuthenticationBackend'
223 OIDC_OP_LOGOUT_URL_METHOD = 'clb_auth.auth.logout_url'
224
225 {{/code}}
226
227
228 === OIDC Plugin for Python : flask-oidc ===
229
230 [[https:~~/~~/flask-oidc.readthedocs.io/en/latest/>>https://flask-oidc.readthedocs.io/en/latest/]]
231
232 You will need at the root of your project a "client_secrets.json" file.
233
234 ==== ====
235
236 client_secrets.json
237
238 {{code language="json"}}
239 {
240 "web": {
241 "client_id": "yourClientId",
242 "client_secret": "yourClientSecret",
243 "redirect_uris": [
244 "http://localhost:8081/",
245 "https://yourappurl.url/"
246 ],
247 "auth_uri": "https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/auth",
248 "token_uri": "https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/token",
249 "userinfo_uri": "https://iam.ebrains.eu/auth/realms/hbp/protocol/openid-connect/userinfo",
250 "issuer": "https://iam.ebrains.eu/auth/realms/hbp"
251 }
252 }
253
254 {{/code}}