I'm attempting to use OAuth with the optional service HTTP header (X-service or service) using the "client_credentials" grant type with CAS v6.0.
I have three simple services registered: HTTP-100.json { "@class" : "org.apereo.cas.services.RegexRegisteredService", "serviceId" : "^(http|https)://.*", "name" : "HTTP", "id" : 100, "description" : "This service definition authorizes all application urls that support HTTP or HTTPS protocols.", "evaluationOrder" : 10000 } OAUTH_CLIENT-101.json { "@class" : "org.apereo.cas.support.oauth.services.OAuthRegisteredService", "clientId": "scdb_api_v1", "clientSecret": "clientSecret11", "name" : "OAUTH_CLIENT", "id" : 101, "serviceId" : "http://example.com/api/v1", "supportedGrantTypes": [ "java.util.HashSet", [ "client_credentials" ] ], "jsonFormat":true } OAUTH_CLIENT-102.json { "@class" : "org.apereo.cas.support.oauth.services.OAuthRegisteredService", "clientId": "scdb_api_v2", "clientSecret": "clientSecret22", "name" : "OAUTH_CLIENT", "id" : 102, "serviceId" : "http://example.com/api/v2", "supportedGrantTypes": [ "java.util.HashSet", [ "client_credentials" ] ], "jsonFormat":true } According to the documentation <https://apereo.github.io/cas/6.0.x/installation/OAuth-OpenId-Authentication.html>: "You may optionally also pass along a service or X-service header value that identifies the target application url. The header value must match the OAuth service definition in the registry that is linked to the client id." When I execute the attached Python script I get results which do not enforce that the X-service header match the defined serviceId, instead it seems it simply must match ANY serviceId. # TEST 1 Request access token for "X-service: http://example.com/api/v1", with credentials for API v1 Response status code: 200, data: {"access_token": "AT-16-Yz-0nTnaoeDQLMledeZByMHht1T6cL-L","token_type":"bearer","expires_in": 28800,"scope":[]} Request profile for token: AT-16-Yz-0nTnaoeDQLMledeZByMHht1T6cL-L Response status code: 200: data: {"service":"http://example.com/api/v1", "attributes":{},"id":"scdb_api_v1","client_id":"scdb_api_v1"} # TEST 2 Request access token for "X-service: http://example.com/api/v2", but with credentials for API v1 Response status code: 200, data: {"access_token": "AT-17-PWxfLaWN4P1mxf5fKG3qamCSfxJEclsC","token_type":"bearer","expires_in": 28800,"scope":[]} Request profile for token: AT-17-PWxfLaWN4P1mxf5fKG3qamCSfxJEclsC Response status code: 200, data: {"service":"http://example.com/api/v2", "attributes":{},"id":"scdb_api_v1","client_id":"scdb_api_v2"} # TEST 3 Request access token for "X-service: http://example.com/api/v3", but with credentials for API v1 Response status code: 200, data: {"access_token": "AT-18-pba2h3sGLPfoVRw-HEqveQ-tplPBk9De","token_type":"bearer","expires_in": 28800,"scope":[]} Request profile for token: AT-18-pba2h3sGLPfoVRw-HEqveQ-tplPBk9De Response status code: 200, data: {"attributes":{},"id":"scdb_api_v1"} As the results demonstrate I can use the credentials for one service to authenticate for another service! Perhaps I have misunderstood how the X-service header is meant to be used, or how services are resolved. But my expectation was that only TEST 1 would be able to successfully authenticate. Any insight into this matter would be greatly appreciated. Thanks, -Dylan -- - Website: https://apereo.github.io/cas - Gitter Chatroom: https://gitter.im/apereo/cas - List Guidelines: https://goo.gl/1VRrw7 - Contributions: https://goo.gl/mh7qDG --- You received this message because you are subscribed to the Google Groups "CAS Community" group. To unsubscribe from this group and stop receiving emails from it, send an email to cas-user+unsubscr...@apereo.org. To view this discussion on the web visit https://groups.google.com/a/apereo.org/d/msgid/cas-user/a5bb8079-ada5-46df-a0b5-64e480311fdd%40apereo.org.
#!/usr/bin/env python3 # # Example usage of CAS OAuth 2.0 for authentication. # import sys import json import uuid from datetime import date from pprint import pprint from getpass import getpass import requests CAS_BASE_URL = 'https://10.0.8.194:8443/cas' # HTTP-100.json # { # "@class" : "org.apereo.cas.services.RegexRegisteredService", # "serviceId" : "^(http|https)://.*", # "name" : "HTTP", # "id" : 100, # "description" : "This service definition authorizes all application urls that support HTTP or HTTPS protocols.", # "evaluationOrder" : 10000 # } # OAUTH_CLIENT-101.json # { # "@class" : "org.apereo.cas.support.oauth.services.OAuthRegisteredService", # "clientId": "scdb_api_v1", # "clientSecret": "clientSecret11", # "name" : "OAUTH_CLIENT", # "id" : 101, # "serviceId" : "http://example.com/api/v1", # "supportedGrantTypes": [ "java.util.HashSet", [ "client_credentials" ] ], # "jsonFormat":true # } # OAUTH_CLIENT-102 # { # "@class" : "org.apereo.cas.support.oauth.services.OAuthRegisteredService", # "clientId": "scdb_api_v2", # "clientSecret": "clientSecret22", # "name" : "OAUTH_CLIENT", # "id" : 102, # "serviceId" : "http://example.com/api/v2", # "supportedGrantTypes": [ "java.util.HashSet", [ "client_credentials" ] ], # "jsonFormat":true # } # Test #1 print('Request access token for "X-service: http://example.com/api/v1", with credentials for API v1') res = requests.post(CAS_BASE_URL + '/oauth2.0/accessToken', verify=False, headers={ 'Accept':'application/json', 'X-service': 'http://example.com/api/v1' }, data={ 'grant_type':'client_credentials', 'client_id':'scdb_api_v1', 'client_secret': 'clientSecret11', } ) print('Response status code:', res.status_code) print(res.text) print() if res.status_code != 200: sys.exit(1) data = res.json() print('Request profile for token:', data['access_token']) res = requests.get(CAS_BASE_URL + '/oauth2.0/profile', verify=False, headers={ 'Accept':'application/json', }, params={ 'access_token' : data['access_token'] } ) print('Response status code:', res.status_code) print(res.text) print() if res.status_code != 200: sys.exit(1) # Test #2 print('Request access token for "X-service: http://example.com/api/v2", but with credentials for API v1') res = requests.post(CAS_BASE_URL + '/oauth2.0/accessToken', verify=False, headers={ 'Accept':'application/json', 'X-service': 'http://example.com/api/v2' }, data={ 'grant_type':'client_credentials', 'client_id':'scdb_api_v1', 'client_secret': 'clientSecret11', } ) print('Response status code:', res.status_code) print(res.text) print() if res.status_code != 200: sys.exit(1) data = res.json() print('Request profile for token:', data['access_token']) res = requests.get(CAS_BASE_URL + '/oauth2.0/profile', verify=False, headers={ 'Accept':'application/json', }, params={ 'access_token' : data['access_token'] } ) print('Response status code:', res.status_code) print(res.text) print() if res.status_code != 200: sys.exit(1) # Test #3 print('Request access token for "X-service: http://example.com/api/v3", but with credentials for API v1') res = requests.post(CAS_BASE_URL + '/oauth2.0/accessToken', verify=False, headers={ 'Accept':'application/json', 'X-service': 'http://example.com/api/v3' }, data={ 'grant_type':'client_credentials', 'client_id':'scdb_api_v1', 'client_secret': 'clientSecret11', } ) print('Response status code:', res.status_code) print(res.text) print() if res.status_code != 200: sys.exit(1) data = res.json() print('Request profile for token:', data['access_token']) res = requests.get(CAS_BASE_URL + '/oauth2.0/profile', verify=False, headers={ 'Accept':'application/json', }, params={ 'access_token' : data['access_token'] } ) print('Response status code:', res.status_code) print(res.text) print() if res.status_code != 200: sys.exit(1)