Skip to content

Commit 83def2a

Browse files
committed
feat: user creation flow support
1 parent 12b8d48 commit 83def2a

File tree

3 files changed

+95
-4
lines changed

3 files changed

+95
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Versions follow [Semantic Versioning](https://semver.org/>) (<major>.<minor>.<pa
1313

1414
- Implement `test_client` attribute.
1515
- Implement `logout` method.
16+
- Support for OIDC `create` prompt.
1617

1718
## [0.1.1] - 2025-04-04
1819

doc/client-applications.rst

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ client registration. Here is an example of dynamic registration you can implemen
109109
client_id = response.json["client_id"]
110110
client_secret = response.json["client_secret"]
111111
112-
Nominal authentication case
113-
---------------------------
112+
Nominal authentication workflow
113+
-------------------------------
114114

115115
Let us suppose that your application have a ``/protected`` that redirects users
116116
to the IAM server if unauthenticated.
@@ -180,8 +180,8 @@ They allow you to skip the login, password and consent pages:
180180
# 4. now you have access to the protected page
181181
res = test_client.get("/protected")
182182
183-
Error cases
184-
-----------
183+
Authentication workflow errors
184+
------------------------------
185185

186186
The `OAuth2 <https://datatracker.ietf.org/doc/html/rfc6749>`_ and the `OpenID Connect <https://openid.net/specs/openid-connect-core-1_0.html>`_ specifications details how things might go wrong:
187187

@@ -209,3 +209,49 @@ The `OIDC error codes <https://openid.net/specs/openid-connect-core-1_0.html#Aut
209209

210210
You might or might not be interested in testing how your application behaves when it encounters those situations,
211211
depending on the situation and how much you trust the libraries that helps your application perform the authentication process.
212+
213+
Account creation workflow
214+
-------------------------
215+
216+
The `Initiating User Registration via OpenID Connect 1.0 <https://openid.net/specs/openid-connect-prompt-create-1_0.html>`_
217+
specification details how to initiate an account creation workflow at the IAM
218+
by setting the ``prompt=create`` authorization request parameter.
219+
220+
In the following example, we suppose that the ``/create`` endpoint redirects
221+
to the IAM authorization endpoint with the ``prompt=create`` parameters.
222+
223+
.. code-block:: python
224+
:caption: Account creation workflow
225+
226+
def test_account_creation(iam_server, client, test_client):
227+
# access to the client account creation page
228+
res = test_client.get("/create")
229+
230+
# redirection to the IAM account creation page
231+
res = iam_server.test_client.get(res.location)
232+
233+
# redirection to the account creation page
234+
res = iam_server.test_client.get(res.location)
235+
236+
payload = {
237+
"user_name": "user",
238+
"given_name": "John",
239+
"family_name": "Doe",
240+
"emails-0": "[email protected]",
241+
"preferred_language": "auto", # appears to be mandatory
242+
"password1": "correct horse battery staple",
243+
"password2": "correct horse battery staple",
244+
}
245+
246+
# fill the registration form
247+
res = iam_server.test_client.post(res.location, data=payload)
248+
249+
# fill the 'consent' form
250+
res = iam_server.test_client.post(res.location, data={"answer": "accept"})
251+
252+
# return to the client with a code
253+
res = test_client.get(res.location)
254+
255+
assert "User account successfully created" in res.text
256+
257+
Unfortunately there is no helpers for account creation in the fashion of :meth:`~pytest_iam.Server.login`.

tests/test_client_application.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ def login():
7676
url_for("authorize", _external=True)
7777
)
7878

79+
@app.route("/create")
80+
def create():
81+
return oauth.authorization_server.authorize_redirect(
82+
url_for("authorize", _external=True), prompt="create"
83+
)
84+
7985
@app.route("/authorize")
8086
def authorize():
8187
token = oauth.authorization_server.authorize_access_token()
@@ -139,10 +145,48 @@ def test_prelogin_and_preconsent(iam_server, client, user, test_client):
139145
# return to the client with a code
140146
res = test_client.get(res.location)
141147

148+
iam_server.logout()
149+
150+
151+
def test_account_creation(iam_server, client, test_client):
152+
# access to the client account creation page
153+
res = test_client.get("/create")
154+
155+
# redirection to the IAM account creation page
156+
res = iam_server.test_client.get(res.location)
157+
158+
# redirection to the account creation page
159+
res = iam_server.test_client.get(res.location)
160+
161+
payload = {
162+
"user_name": "user",
163+
"given_name": "John",
164+
"family_name": "Doe",
165+
"emails-0": "[email protected]",
166+
"preferred_language": "auto", # appears to be mandatory
167+
"password1": "correct horse battery staple",
168+
"password2": "correct horse battery staple",
169+
}
170+
171+
# fill the registration form
172+
res = iam_server.test_client.post(res.location, data=payload)
173+
174+
# fill the 'consent' form
175+
res = iam_server.test_client.post(res.location, data={"answer": "accept"})
176+
177+
authorization = iam_server.backend.get(iam_server.models.AuthorizationCode)
178+
assert authorization.client == client
179+
180+
# return to the client with a code
181+
res = test_client.get(res.location)
182+
142183
token = iam_server.backend.get(iam_server.models.Token)
143184
assert token.client == client
144185

145186
assert res.json["userinfo"]["sub"] == "user"
146187
assert res.json["access_token"] == token.access_token
147188

189+
user = iam_server.backend.get(iam_server.models.User)
190+
assert user.user_name == "user"
191+
148192
iam_server.logout()

0 commit comments

Comments
 (0)