authentik.sources.oauth.tests.test_type_slack
Slack Type tests
1"""Slack Type tests""" 2 3from unittest.mock import patch 4 5from django.test import TestCase 6 7from authentik.sources.oauth.models import OAuthSource 8from authentik.sources.oauth.types.slack import ( 9 SlackOAuth2Callback, 10 SlackOAuthClient, 11 SlackType, 12) 13 14# https://api.slack.com/methods/openid.connect.userInfo 15SLACK_USER = { 16 "ok": True, 17 "sub": "U09VAHA70UU", 18 "https://slack.com/user_id": "U09VAHA70UU", 19 "https://slack.com/team_id": "T08G285D7DX", 20 "email": "user@example.com", 21 "email_verified": True, 22 "name": "Test User", 23 "picture": "https://secure.gravatar.com/avatar/test.jpg", 24 "given_name": "Test", 25 "family_name": "User", 26 "locale": "en-US", 27 "https://slack.com/team_name": "Test Workspace", 28 "https://slack.com/team_domain": "test-workspace", 29} 30 31# https://api.slack.com/methods/oauth.v2.access 32# Slack oauth.v2.access response with user token (Sign in with Slack, no token rotation) 33SLACK_TOKEN_RESPONSE = { 34 "ok": True, 35 "app_id": "A0118NQPZZC", 36 "authed_user": { 37 "id": "U065VRX1T0", 38 "scope": "openid,email,profile", 39 "access_token": "xoxp-user-token-12345", 40 "token_type": "user", 41 }, 42 "team": {"id": "T024BE7LD"}, 43 "enterprise": None, 44 "is_enterprise_install": False, 45} 46 47# https://api.slack.com/methods/oauth.v2.access 48# https://api.slack.com/authentication/rotation 49# Slack oauth.v2.access response with token rotation enabled 50SLACK_TOKEN_RESPONSE_WITH_REFRESH = { 51 "ok": True, 52 "app_id": "A0KRD7HC3", 53 "authed_user": { 54 "id": "U1234", 55 "scope": "openid,email,profile", 56 "access_token": "xoxe.xoxp-1234", 57 "refresh_token": "xoxe-1-refresh-token", 58 "token_type": "user", 59 "expires_in": 43200, 60 }, 61 "team": {"id": "T9TK3CUKW", "name": "Slack Softball Team"}, 62 "enterprise": None, 63 "is_enterprise_install": False, 64} 65 66 67class TestTypeSlack(TestCase): 68 """Slack OAuth Source tests""" 69 70 def setUp(self): 71 self.source = OAuthSource.objects.create( 72 name="test", 73 slug="test", 74 provider_type="slack", 75 ) 76 77 def test_enroll_context(self): 78 """Test Slack enrollment context""" 79 ak_context = SlackType().get_base_user_properties( 80 source=self.source, info=SLACK_USER, token={} 81 ) 82 self.assertEqual(ak_context["username"], SLACK_USER["name"]) 83 self.assertEqual(ak_context["email"], SLACK_USER["email"]) 84 self.assertEqual(ak_context["name"], SLACK_USER["name"]) 85 86 def test_get_user_id(self): 87 """Test Slack user ID extraction from profile info""" 88 callback = SlackOAuth2Callback() 89 # Test with 'sub' field (OIDC userinfo response) 90 self.assertEqual(callback.get_user_id({"sub": "U12345"}), "U12345") 91 # Test with no sub 92 self.assertIsNone(callback.get_user_id({})) 93 94 def test_token_extraction_user_token(self): 95 """Test that user token is extracted from nested authed_user""" 96 client = SlackOAuthClient(self.source, None) 97 98 # Mock the parent class method to return Slack's nested response 99 with patch( 100 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 101 ) as mock_parent: 102 mock_parent.return_value = SLACK_TOKEN_RESPONSE.copy() 103 104 token = client.get_access_token() 105 106 # Verify user token was extracted to top level 107 self.assertEqual(token["access_token"], "xoxp-user-token-12345") 108 # Verify token_type was normalized to Bearer 109 self.assertEqual(token["token_type"], "Bearer") 110 # Verify user ID was extracted 111 self.assertEqual(token["id"], "U065VRX1T0") 112 113 def test_token_extraction_with_refresh(self): 114 """Test that refresh_token and expires_in are extracted when present""" 115 client = SlackOAuthClient(self.source, None) 116 117 with patch( 118 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 119 ) as mock_parent: 120 mock_parent.return_value = SLACK_TOKEN_RESPONSE_WITH_REFRESH.copy() 121 122 token = client.get_access_token() 123 124 # Verify tokens were extracted from authed_user 125 self.assertEqual(token["access_token"], "xoxe.xoxp-1234") 126 self.assertEqual(token["refresh_token"], "xoxe-1-refresh-token") 127 self.assertEqual(token["expires_in"], 43200) 128 self.assertEqual(token["token_type"], "Bearer") 129 self.assertEqual(token["id"], "U1234") 130 131 def test_token_type_always_bearer(self): 132 """Test that token_type is always set to Bearer""" 133 client = SlackOAuthClient(self.source, None) 134 135 # Test with authed_user response 136 with patch( 137 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 138 ) as mock_parent: 139 mock_parent.return_value = { 140 "ok": True, 141 "authed_user": { 142 "id": "U12345", 143 "access_token": "xoxp-test", 144 "token_type": "user", # Slack returns "user" 145 }, 146 } 147 token = client.get_access_token() 148 self.assertEqual(token["token_type"], "Bearer") 149 150 # Test with bot token response (no authed_user) 151 with patch( 152 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 153 ) as mock_parent: 154 mock_parent.return_value = { 155 "ok": True, 156 "access_token": "xoxb-bot-token", 157 # No token_type in response 158 } 159 token = client.get_access_token() 160 self.assertEqual(token["token_type"], "Bearer") 161 162 def test_token_error_passthrough(self): 163 """Test that error responses are passed through unchanged""" 164 client = SlackOAuthClient(self.source, None) 165 166 with patch( 167 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 168 ) as mock_parent: 169 mock_parent.return_value = {"error": "invalid_grant"} 170 token = client.get_access_token() 171 self.assertEqual(token, {"error": "invalid_grant"}) 172 173 def test_token_none_passthrough(self): 174 """Test that None is passed through""" 175 client = SlackOAuthClient(self.source, None) 176 177 with patch( 178 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 179 ) as mock_parent: 180 mock_parent.return_value = None 181 token = client.get_access_token() 182 self.assertIsNone(token)
SLACK_USER =
{'ok': True, 'sub': 'U09VAHA70UU', 'https://slack.com/user_id': 'U09VAHA70UU', 'https://slack.com/team_id': 'T08G285D7DX', 'email': 'user@example.com', 'email_verified': True, 'name': 'Test User', 'picture': 'https://secure.gravatar.com/avatar/test.jpg', 'given_name': 'Test', 'family_name': 'User', 'locale': 'en-US', 'https://slack.com/team_name': 'Test Workspace', 'https://slack.com/team_domain': 'test-workspace'}
SLACK_TOKEN_RESPONSE =
{'ok': True, 'app_id': 'A0118NQPZZC', 'authed_user': {'id': 'U065VRX1T0', 'scope': 'openid,email,profile', 'access_token': 'xoxp-user-token-12345', 'token_type': 'user'}, 'team': {'id': 'T024BE7LD'}, 'enterprise': None, 'is_enterprise_install': False}
SLACK_TOKEN_RESPONSE_WITH_REFRESH =
{'ok': True, 'app_id': 'A0KRD7HC3', 'authed_user': {'id': 'U1234', 'scope': 'openid,email,profile', 'access_token': 'xoxe.xoxp-1234', 'refresh_token': 'xoxe-1-refresh-token', 'token_type': 'user', 'expires_in': 43200}, 'team': {'id': 'T9TK3CUKW', 'name': 'Slack Softball Team'}, 'enterprise': None, 'is_enterprise_install': False}
class
TestTypeSlack(django.test.testcases.TestCase):
68class TestTypeSlack(TestCase): 69 """Slack OAuth Source tests""" 70 71 def setUp(self): 72 self.source = OAuthSource.objects.create( 73 name="test", 74 slug="test", 75 provider_type="slack", 76 ) 77 78 def test_enroll_context(self): 79 """Test Slack enrollment context""" 80 ak_context = SlackType().get_base_user_properties( 81 source=self.source, info=SLACK_USER, token={} 82 ) 83 self.assertEqual(ak_context["username"], SLACK_USER["name"]) 84 self.assertEqual(ak_context["email"], SLACK_USER["email"]) 85 self.assertEqual(ak_context["name"], SLACK_USER["name"]) 86 87 def test_get_user_id(self): 88 """Test Slack user ID extraction from profile info""" 89 callback = SlackOAuth2Callback() 90 # Test with 'sub' field (OIDC userinfo response) 91 self.assertEqual(callback.get_user_id({"sub": "U12345"}), "U12345") 92 # Test with no sub 93 self.assertIsNone(callback.get_user_id({})) 94 95 def test_token_extraction_user_token(self): 96 """Test that user token is extracted from nested authed_user""" 97 client = SlackOAuthClient(self.source, None) 98 99 # Mock the parent class method to return Slack's nested response 100 with patch( 101 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 102 ) as mock_parent: 103 mock_parent.return_value = SLACK_TOKEN_RESPONSE.copy() 104 105 token = client.get_access_token() 106 107 # Verify user token was extracted to top level 108 self.assertEqual(token["access_token"], "xoxp-user-token-12345") 109 # Verify token_type was normalized to Bearer 110 self.assertEqual(token["token_type"], "Bearer") 111 # Verify user ID was extracted 112 self.assertEqual(token["id"], "U065VRX1T0") 113 114 def test_token_extraction_with_refresh(self): 115 """Test that refresh_token and expires_in are extracted when present""" 116 client = SlackOAuthClient(self.source, None) 117 118 with patch( 119 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 120 ) as mock_parent: 121 mock_parent.return_value = SLACK_TOKEN_RESPONSE_WITH_REFRESH.copy() 122 123 token = client.get_access_token() 124 125 # Verify tokens were extracted from authed_user 126 self.assertEqual(token["access_token"], "xoxe.xoxp-1234") 127 self.assertEqual(token["refresh_token"], "xoxe-1-refresh-token") 128 self.assertEqual(token["expires_in"], 43200) 129 self.assertEqual(token["token_type"], "Bearer") 130 self.assertEqual(token["id"], "U1234") 131 132 def test_token_type_always_bearer(self): 133 """Test that token_type is always set to Bearer""" 134 client = SlackOAuthClient(self.source, None) 135 136 # Test with authed_user response 137 with patch( 138 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 139 ) as mock_parent: 140 mock_parent.return_value = { 141 "ok": True, 142 "authed_user": { 143 "id": "U12345", 144 "access_token": "xoxp-test", 145 "token_type": "user", # Slack returns "user" 146 }, 147 } 148 token = client.get_access_token() 149 self.assertEqual(token["token_type"], "Bearer") 150 151 # Test with bot token response (no authed_user) 152 with patch( 153 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 154 ) as mock_parent: 155 mock_parent.return_value = { 156 "ok": True, 157 "access_token": "xoxb-bot-token", 158 # No token_type in response 159 } 160 token = client.get_access_token() 161 self.assertEqual(token["token_type"], "Bearer") 162 163 def test_token_error_passthrough(self): 164 """Test that error responses are passed through unchanged""" 165 client = SlackOAuthClient(self.source, None) 166 167 with patch( 168 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 169 ) as mock_parent: 170 mock_parent.return_value = {"error": "invalid_grant"} 171 token = client.get_access_token() 172 self.assertEqual(token, {"error": "invalid_grant"}) 173 174 def test_token_none_passthrough(self): 175 """Test that None is passed through""" 176 client = SlackOAuthClient(self.source, None) 177 178 with patch( 179 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 180 ) as mock_parent: 181 mock_parent.return_value = None 182 token = client.get_access_token() 183 self.assertIsNone(token)
Slack OAuth Source tests
def
setUp(self):
71 def setUp(self): 72 self.source = OAuthSource.objects.create( 73 name="test", 74 slug="test", 75 provider_type="slack", 76 )
Hook method for setting up the test fixture before exercising it.
def
test_enroll_context(self):
78 def test_enroll_context(self): 79 """Test Slack enrollment context""" 80 ak_context = SlackType().get_base_user_properties( 81 source=self.source, info=SLACK_USER, token={} 82 ) 83 self.assertEqual(ak_context["username"], SLACK_USER["name"]) 84 self.assertEqual(ak_context["email"], SLACK_USER["email"]) 85 self.assertEqual(ak_context["name"], SLACK_USER["name"])
Test Slack enrollment context
def
test_get_user_id(self):
87 def test_get_user_id(self): 88 """Test Slack user ID extraction from profile info""" 89 callback = SlackOAuth2Callback() 90 # Test with 'sub' field (OIDC userinfo response) 91 self.assertEqual(callback.get_user_id({"sub": "U12345"}), "U12345") 92 # Test with no sub 93 self.assertIsNone(callback.get_user_id({}))
Test Slack user ID extraction from profile info
def
test_token_extraction_user_token(self):
95 def test_token_extraction_user_token(self): 96 """Test that user token is extracted from nested authed_user""" 97 client = SlackOAuthClient(self.source, None) 98 99 # Mock the parent class method to return Slack's nested response 100 with patch( 101 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 102 ) as mock_parent: 103 mock_parent.return_value = SLACK_TOKEN_RESPONSE.copy() 104 105 token = client.get_access_token() 106 107 # Verify user token was extracted to top level 108 self.assertEqual(token["access_token"], "xoxp-user-token-12345") 109 # Verify token_type was normalized to Bearer 110 self.assertEqual(token["token_type"], "Bearer") 111 # Verify user ID was extracted 112 self.assertEqual(token["id"], "U065VRX1T0")
Test that user token is extracted from nested authed_user
def
test_token_extraction_with_refresh(self):
114 def test_token_extraction_with_refresh(self): 115 """Test that refresh_token and expires_in are extracted when present""" 116 client = SlackOAuthClient(self.source, None) 117 118 with patch( 119 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 120 ) as mock_parent: 121 mock_parent.return_value = SLACK_TOKEN_RESPONSE_WITH_REFRESH.copy() 122 123 token = client.get_access_token() 124 125 # Verify tokens were extracted from authed_user 126 self.assertEqual(token["access_token"], "xoxe.xoxp-1234") 127 self.assertEqual(token["refresh_token"], "xoxe-1-refresh-token") 128 self.assertEqual(token["expires_in"], 43200) 129 self.assertEqual(token["token_type"], "Bearer") 130 self.assertEqual(token["id"], "U1234")
Test that refresh_token and expires_in are extracted when present
def
test_token_type_always_bearer(self):
132 def test_token_type_always_bearer(self): 133 """Test that token_type is always set to Bearer""" 134 client = SlackOAuthClient(self.source, None) 135 136 # Test with authed_user response 137 with patch( 138 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 139 ) as mock_parent: 140 mock_parent.return_value = { 141 "ok": True, 142 "authed_user": { 143 "id": "U12345", 144 "access_token": "xoxp-test", 145 "token_type": "user", # Slack returns "user" 146 }, 147 } 148 token = client.get_access_token() 149 self.assertEqual(token["token_type"], "Bearer") 150 151 # Test with bot token response (no authed_user) 152 with patch( 153 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 154 ) as mock_parent: 155 mock_parent.return_value = { 156 "ok": True, 157 "access_token": "xoxb-bot-token", 158 # No token_type in response 159 } 160 token = client.get_access_token() 161 self.assertEqual(token["token_type"], "Bearer")
Test that token_type is always set to Bearer
def
test_token_error_passthrough(self):
163 def test_token_error_passthrough(self): 164 """Test that error responses are passed through unchanged""" 165 client = SlackOAuthClient(self.source, None) 166 167 with patch( 168 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 169 ) as mock_parent: 170 mock_parent.return_value = {"error": "invalid_grant"} 171 token = client.get_access_token() 172 self.assertEqual(token, {"error": "invalid_grant"})
Test that error responses are passed through unchanged
def
test_token_none_passthrough(self):
174 def test_token_none_passthrough(self): 175 """Test that None is passed through""" 176 client = SlackOAuthClient(self.source, None) 177 178 with patch( 179 "authentik.sources.oauth.clients.oauth2.OAuth2Client.get_access_token" 180 ) as mock_parent: 181 mock_parent.return_value = None 182 token = client.get_access_token() 183 self.assertIsNone(token)
Test that None is passed through