Skip to content

Commit 093c732

Browse files
Adding Support For Back Channel Login
1 parent 6c908c9 commit 093c732

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from typing import Any
2+
3+
from .base import AuthenticationBase
4+
5+
6+
class BackChannelLogin(AuthenticationBase):
7+
"""Back-Channel Login endpoint"""
8+
9+
def back_channel_login(
10+
self, binding_message: str, login_hint: str, scope: str, **kwargs
11+
) -> Any:
12+
"""Send a Back-Channel Login.
13+
14+
Args:
15+
binding_message (str): Human-readable string displayed on both the device calling /bc-authorize and the user’s
16+
authentication device to ensure the user is approves the correct request.
17+
18+
login_hint (str): String containing information about the user to contact for authentication.
19+
20+
scope(str): "openid" is a required scope.Multiple scopes are separated
21+
with whitespace.
22+
23+
**kwargs: Other fields to send along with the PAR.
24+
25+
Returns:
26+
auth_req_id, expires_in, interval
27+
"""
28+
return self.authenticated_post(
29+
f"{self.protocol}://{self.domain}/bc-authorize",
30+
data={
31+
"client_id": self.client_id,
32+
"binding_message": binding_message,
33+
"login_hint": login_hint,
34+
"scope": scope,
35+
**kwargs,
36+
},
37+
)

auth0/authentication/get_token.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,27 @@ def passwordless_login(
253253
"grant_type": "http://auth0.com/oauth/grant-type/passwordless/otp",
254254
},
255255
)
256+
257+
def backchannel_login(
258+
self, auth_req_id: str, grant_type: str = "urn:openid:params:grant-type:ciba",
259+
) -> Any:
260+
"""Calls /oauth/token endpoint with "urn:openid:params:grant-type:ciba" grant type
261+
262+
Args:
263+
auth_req_id (str): The id received from /bc-authorize
264+
265+
grant_type (str): Denotes the flow you're using.For Back Channel login
266+
use urn:openid:params:grant-type:ciba
267+
268+
Returns:
269+
access_token, id_token
270+
"""
271+
272+
return self.authenticated_post(
273+
f"{self.protocol}://{self.domain}/oauth/token",
274+
data={
275+
"client_id": self.client_id,
276+
"auth_req_id": auth_req_id,
277+
"grant_type": grant_type,
278+
},
279+
)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
2+
import unittest
3+
from unittest import mock
4+
5+
import requests
6+
from ...exceptions import Auth0Error, RateLimitError
7+
8+
from ...authentication.back_channel_login import BackChannelLogin
9+
10+
class TestBackChannelLogin(unittest.TestCase):
11+
@mock.patch("auth0.rest.RestClient.post")
12+
def test_ciba(self, mock_post):
13+
g = BackChannelLogin("my.domain.com", "cid", client_secret="clsec")
14+
15+
g.back_channel_login(
16+
binding_message="This is a binding message",
17+
login_hint="{ \"format\": \"iss_sub\", \"iss\": \"https://my.domain.auth0.com/\", \"sub\": \"auth0|[USER ID]\" }",
18+
scope="openid",
19+
)
20+
21+
args, kwargs = mock_post.call_args
22+
23+
self.assertEqual(args[0], "https://my.domain.com/bc-authorize")
24+
self.assertEqual(
25+
kwargs["data"],
26+
{
27+
"client_id": "cid",
28+
"client_secret": "clsec",
29+
"binding_message": "This is a binding message",
30+
"login_hint": "{ \"format\": \"iss_sub\", \"iss\": \"https://my.domain.auth0.com/\", \"sub\": \"auth0|[USER ID]\" }",
31+
"scope": "openid",
32+
},
33+
)
34+
35+
@mock.patch("auth0.rest.RestClient.post")
36+
def test_should_require_binding_message(self, mock_post):
37+
g = BackChannelLogin("my.domain.com", "cid", client_secret="clsec")
38+
39+
# Expecting an exception to be raised when binding_message is missing
40+
with self.assertRaises(Exception) as context:
41+
g.back_channel_login(
42+
login_hint='{ "format": "iss_sub", "iss": "https://my.domain.auth0.com/", "sub": "auth0|USER_ID" }',
43+
scope="openid",
44+
)
45+
46+
# Assert the error message is correct
47+
self.assertIn("missing 1 required positional argument: \'binding_message\'", str(context.exception))
48+
49+
@mock.patch("auth0.rest.RestClient.post")
50+
def test_should_require_login_hint(self, mock_post):
51+
g = BackChannelLogin("my.domain.com", "cid", client_secret="clsec")
52+
53+
# Expecting an exception to be raised when login_hint is missing
54+
with self.assertRaises(Exception) as context:
55+
g.back_channel_login(
56+
binding_message="This is a binding message.",
57+
scope="openid",
58+
)
59+
60+
# Assert the error message is correct
61+
self.assertIn("missing 1 required positional argument: \'login_hint\'", str(context.exception))
62+
63+
@mock.patch("auth0.rest.RestClient.post")
64+
def test_should_require_scope(self, mock_post):
65+
g = BackChannelLogin("my.domain.com", "cid", client_secret="clsec")
66+
67+
# Expecting an exception to be raised when scope is missing
68+
with self.assertRaises(Exception) as context:
69+
g.back_channel_login(
70+
binding_message="This is a binding message.",
71+
login_hint='{ "format": "iss_sub", "iss": "https://my.domain.auth0.com/", "sub": "auth0|USER_ID" }',
72+
)
73+
74+
# Assert the error message is correct
75+
self.assertIn("missing 1 required positional argument: \'scope\'", str(context.exception))
76+
77+
78+

auth0/test/authentication/test_get_token.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,25 @@ def test_passwordless_login_with_email(self, mock_post):
313313
"scope": "openid",
314314
},
315315
)
316+
317+
@mock.patch("auth0.rest.RestClient.post")
318+
def test_backchannel_login(self, mock_post):
319+
g = GetToken("my.domain.com", "cid", client_secret="csec")
320+
321+
g.backchannel_login(
322+
auth_req_id="reqid",
323+
grant_type="urn:openid:params:grant-type:ciba",
324+
)
325+
326+
args, kwargs = mock_post.call_args
327+
328+
self.assertEqual(args[0], "https://my.domain.com/oauth/token")
329+
self.assertEqual(
330+
kwargs["data"],
331+
{
332+
"client_id": "cid",
333+
"client_secret": "csec",
334+
"auth_req_id": "reqid",
335+
"grant_type": "urn:openid:params:grant-type:ciba",
336+
},
337+
)

0 commit comments

Comments
 (0)