FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: add get_profile_and_token_from_id_token for OIDC ID token excha… · workos/workos-python@51434dd · GitHub

Commit 51434dd

Browse files
feat: add get_profile_and_token_from_id_token for OIDC ID token exchange (4.16.0 backport) (#714)
1 parent 5ed2044 commit 51434dd

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

‎tests/test_sso.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,40 @@ def test_get_profile_and_token_returns_expected_workosprofile_object(
424424
assert profile_and_token.access_token == "01DY34ACQTM3B1CSX1YSZ8Z00D"
425425
assert profile_and_token.profile.to_dict() == mock_profile
426426

427+
def test_get_profile_and_token_from_id_token_returns_expected_workosprofile_object(
428+
self, setup_with_client_id, mock_profile, mock_request_method
429+
):
430+
response_dict = {
431+
"profile": {
432+
"object": "profile",
433+
"id": mock_profile["id"],
434+
"email": mock_profile["email"],
435+
"first_name": mock_profile["first_name"],
436+
"groups": mock_profile["groups"],
437+
"organization_id": mock_profile["organization_id"],
438+
"connection_id": mock_profile["connection_id"],
439+
"connection_type": mock_profile["connection_type"],
440+
"last_name": mock_profile["last_name"],
441+
"idp_id": mock_profile["idp_id"],
442+
"raw_attributes": {
443+
"email": mock_profile["raw_attributes"]["email"],
444+
"first_name": mock_profile["raw_attributes"]["first_name"],
445+
"last_name": mock_profile["raw_attributes"]["last_name"],
446+
"groups": mock_profile["raw_attributes"]["groups"],
447+
},
448+
},
449+
"access_token": "01DY34ACQTM3B1CSX1YSZ8Z00D",
450+
}
451+
452+
mock_request_method("post", response_dict, 200)
453+
454+
profile_and_token = self.sso.get_profile_and_token_from_id_token(
455+
"eyJhbGciOiJSUzI1NiJ9.id.token", "org_01EHQMYV6MBK39QC5PZXHY59C3"
456+
)
457+
458+
assert profile_and_token.access_token == "01DY34ACQTM3B1CSX1YSZ8Z00D"
459+
assert profile_and_token.profile.to_dict() == mock_profile
460+
427461
def test_get_profile_and_token_without_first_name_or_last_name_returns_expected_workosprofile_object(
428462
self, setup_with_client_id, mock_magic_link_profile, mock_request_method
429463
):

‎workos/__about__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
__package_url__ = "https://github.com/workos-inc/workos-python"
1414

15-
__version__ = "4.15.0"
15+
__version__ = "4.16.0"
1616

1717
__author__ = "WorkOS"
1818

‎workos/sso.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
PROFILE_PATH = "sso/profile"
2525

2626
OAUTH_GRANT_TYPE = "authorization_code"
27+
TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"
28+
ID_TOKEN_SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:id_token"
2729

2830
RESPONSE_LIMIT = 10
2931

@@ -162,6 +164,39 @@ def get_profile_and_token(self, code):
162164

163165
return WorkOSProfileAndToken.construct_from_response(response)
164166

167+
def get_profile_and_token_from_id_token(self, id_token, organization_id):
168+
"""Exchange an externally issued OIDC ID token for a Profile and Token
169+
170+
For flows where the user authenticates natively with the identity
171+
provider (for example a mobile app using MSAL against Microsoft Entra)
172+
and no browser redirect occurs. The ID token is exchanged for the same
173+
WorkOS profile the authorization code flow would return. The connection
174+
must have an ID token trust configured for the token's issuer and
175+
audience.
176+
177+
Args:
178+
id_token (str): The OIDC ID token issued to the client.
179+
organization_id (str): The organization whose connection the ID
180+
token should be validated against.
181+
182+
Returns:
183+
WorkOSProfileAndToken: WorkOSProfileAndToken object representing the User
184+
"""
185+
params = {
186+
"client_id": workos.client_id,
187+
"client_secret": workos.api_key,
188+
"grant_type": TOKEN_EXCHANGE_GRANT_TYPE,
189+
"subject_token": id_token,
190+
"subject_token_type": ID_TOKEN_SUBJECT_TOKEN_TYPE,
191+
"organization_id": organization_id,
192+
}
193+
194+
response = self.request_helper.request(
195+
TOKEN_PATH, method=REQUEST_METHOD_POST, params=params
196+
)
197+
198+
return WorkOSProfileAndToken.construct_from_response(response)
199+
165200
def get_connection(self, connection):
166201
"""Gets details for a single Connection
167202

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL