-
Notifications
You must be signed in to change notification settings - Fork 150
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
506 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
# External Authn | ||
|
||
## Design | ||
``` | ||
Title External Authn | ||
actor Person | ||
participant Browser | ||
participant Website1 | ||
participant Website2 | ||
participant IDP | ||
database cache | ||
Person->Browser: 1. Navigate to website1 | ||
Browser->Website1: | ||
Website1->Browser: 2. redirect | ||
group Person Authn Script : Step One | ||
Browser->IDP: 3. [GET] /oxauth/authorize?client_id=__&redirect_uri=__&state=__\n&nonce=__&prompt=none&scope=__\n&response_mode=__&response_type=__ | ||
IDP->IDP: 4. generate jans_key | ||
IDP<->cache: cache in session context\njans_key: {request params} | ||
IDP->Browser: 5. redirect /internal.idp?________\nSet Pre-Authn Session Cookie | ||
Browser->Website2: | ||
end | ||
Website2->Browser: 6. Display login page | ||
Person->Browser: 7. Enter Username / PW | ||
Browser->Website2: 8. (creds) | ||
group ROPW script | ||
Website2->IDP: 9. /oxauth/token?uid=__&pw="__&browser_ip=__&jans_key=__ | ||
IDP->IDP: 10. update cache:\n "jans_key": "auth_success" | ||
IDP->IDP: 11. retreive user claims | ||
IDP->Website2:12. {\n "callback_url":"https://op-host/oxauth**/authorize?session_id={session_id}&redirect_uri={original_redirect}&...**",\n "userinfo": {"uid": "__",...}\n } | ||
end | ||
group Person Authn Script Step 2 | ||
Website2->Browser: 13. write website 2 cookie;\n302 Location IDP callback_url | ||
Browser->IDP: 14. callback_url_from_step_12 | ||
IDP->IDP: 15. get session context | ||
IDP->cache:16. delete jans_key\n lookup original redirect_uri | ||
IDP->Browser: 17. write IDP session cookie\nand 302: Location original redirect_uri | ||
end | ||
Browser->Website1: | ||
Website1->Website1: optional: 18 Validate id_token\n (claims optional) | ||
``` | ||
![](external-authn-diagram.png) | ||
|
||
Follow the instructions below to set up: | ||
|
||
## oxAuth Configuration | ||
Above the **ox-auth** record of the **oxAuthConfiguration** table, enable a new attribute on the **oxAuthConfDynamic** field | ||
|
||
Example: | ||
``` | ||
"authorizationRequestCustomAllowedParameters": [ | ||
{ | ||
"paramName": "jansKey", | ||
"returnInResponse": false | ||
} | ||
] | ||
``` | ||
|
||
## Enable Custom Script | ||
|
||
- ### Person Authentication - External Authn | ||
|
||
Create a new record in table **oxCustomScript**. | ||
``` | ||
INSERT INTO oxCustomScript ( doc_id, objectClass, dn, displayName, oxEnabled, oxRevision, oxScript, oxAlias, oxScriptType, oxModuleProperty, programmingLanguage, oxScriptError, oxConfigurationProperty, inum, description, oxLevel ) | ||
VALUES ( 'PA01-EA01', 'oxCustomScript', 'inum=PA01-EA01,ou=scripts,o=gluu', 'pa-external-authn', 0, 1, '', '{"v": []}', 'person_authentication', '{"v": ["{\\"value1\\":\\"usage_type\\",\\"value2\\":\\"interactive\\",\\"description\\":\\"\\"}", "{\\"value1\\":\\"location_type\\",\\"value2\\":\\"ldap\\",\\"description\\":\\"\\"}"]}', 'python', NULL, '{"v": ["{\\"value1\\":\\"urlstep1\\",\\"value2\\":\\"http://demoexample.net:81\\",\\"hide\\":false,\\"description\\":\\"Url to return in step 1\\"}"]}', 'PA01-EA01', 'PA External Authn', 10 ); | ||
``` | ||
|
||
Modify the **oxConfigurationProperty** field by replacing **URL_REDIRECT_URI** with the url that you want to return to the first step | ||
``` | ||
'{"v": ["{\\"value1\\":\\"urlstep1\\",\\"value2\\":\\"{URL_REDIRECT_URI}\\",\\"hide\\":false,\\"description\\":\\"Url to return in step 1\\"}"]}' | ||
``` | ||
|
||
Modify the **oxScript** field by adding the content of the following link: [PersonAuthentication Script](pa-external-authn.py) | ||
|
||
- ### ROPC (Resource Owner Password Credentials) Script - External Authn | ||
|
||
Create a new record in table **oxCustomScript**. | ||
``` | ||
INSERT INTO oxCustomScript ( doc_id, objectClass, dn, displayName, oxEnabled, oxRevision, oxScript, oxAlias, oxScriptType, oxModuleProperty, programmingLanguage, oxScriptError, oxConfigurationProperty, inum, description, oxLevel ) | ||
VALUES ( 'ROPC-EA01', 'oxCustomScript', 'inum=ROPC-EA01,ou=scripts,o=gluu', 'ropc-external-authn', 0, 1, '', '{"v": []}', 'resource_owner_password_credentials', '{"v": ["{\\"value1\\":\\"location_type\\",\\"value2\\":\\"ldap\\",\\"description\\":\\"\\"}"]}', 'python', NULL, '{"v": []}', 'ROPC-EA01', 'ROPC External Authn', 1 ); | ||
``` | ||
|
||
Modify the **oxScript** field by adding the content of the following link: [ROPC (Resource Owner Password Credentials) Script](ropc-external-authn.py) | ||
|
||
- ### Update Token Script - External Authn | ||
|
||
Create a new record in table **oxCustomScript**. | ||
``` | ||
INSERT INTO oxCustomScript ( doc_id, objectClass, dn, displayName, oxEnabled, oxRevision, oxScript, oxAlias, oxScriptType, oxModuleProperty, programmingLanguage, oxScriptError, oxConfigurationProperty, inum, description, oxLevel ) | ||
VALUES ( 'UPDT-EA01', 'oxCustomScript', 'inum=UPDT-EA01,ou=scripts,o=gluu', 'update-token-external-authn', 0, 1, '', '{"v": []}', 'update_token', '{"v": ["{\\"value1\\":\\"location_type\\",\\"value2\\":\\"ldap\\",\\"description\\":\\"\\"}"]}', 'python', NULL, '{"v": []}', 'UPDT-EA01', 'Update token External Authn', 1 ); | ||
``` | ||
|
||
Modify the **oxScript** field by adding the content of the following link: [Update Token Script](ut-external-authn.py) | ||
|
||
In this script you can choose whether to use the header or payload of the **id_token** for the **callback_url**: | ||
``` | ||
jsonWebResponse.getHeader().setClaim("callback_url", jsonValCallbackUrl) | ||
jsonWebResponse.getClaims().setClaim("callback_url", jsonValCallbackUrl) | ||
``` | ||
|
||
## Enable custom script on the Client | ||
In the table **oxAuthClient** modify the field **oxAttributes** associating the custom scripts configured previously | ||
``` | ||
"updateTokenScriptDns": ["inum=UPDT-EA01,ou=scripts,o=gluu"], | ||
"ropcScripts": ["inum=ROPC-EA01,ou=scripts,o=gluu"], | ||
``` | ||
|
||
And in the field **oxAuthDefaultAcrValues** add this value: | ||
``` | ||
{"v": ["pa-external-authn"]} | ||
``` | ||
|
||
## Call Flow | ||
- ### Step 1: /authorize | ||
Request: | ||
``` | ||
curl --location --request GET 'https://{your-gluu-url}/oxauth/restv1/authorize?response_type=code&client_id=14e36e18-1d51-41ac-a4cf-a7dc677f53a5&scope=openid+profile+address+email&redirect_uri=https://jans.localhost/jans-auth-rp/home.htm&state=a84dd61f-533c-46a4-9315-a66fda3e9a4e&nonce=80e6bd2b-eb78-48b9-be9c-6bb33ef80991&ui_locales=&claims_locales=&request_session_id=false&acr_values=' | ||
``` | ||
Response: (return the **redirect_uri** with jansKey) | ||
``` | ||
http://demoexample.net:81?jansKey=46340f40-a554-46b1-9246-37c2e869919f | ||
``` | ||
|
||
- ### Step 2: /token | ||
Request: (**Authorization** = Basic base64(client_id:client_secret)) | ||
``` | ||
curl --location --request POST 'https://{your-gluu-url}/oxauth/restv1/token' \ | ||
--header 'Authorization: Basic MTRlMzZlMTgtMWQ1MS00MWFjLWE0Y2YtYTdkYzY3N2Y1M2E1Ojk5NzE4NWU1LTc2NGUtNGE4Yi1hNjYwLTdjZmQ4NzJhNjc0Ng==' \ | ||
--header 'Content-Type: application/x-www-form-urlencoded' \ | ||
--data-urlencode 'grant_type=password' \ | ||
--data-urlencode 'username=test_user' \ | ||
--data-urlencode 'password=test_user_password' \ | ||
--data-urlencode 'scope=openid' \ | ||
--data-urlencode 'jansKey=46340f40-a554-46b1-9246-37c2e869919f' | ||
``` | ||
Response: (id_token contains in header or payload callback_url) | ||
``` | ||
{ | ||
"access_token": "a0878887-b998-4da4-aa0b-4e74bd9a4441", | ||
"refresh_token": "d8b618ac-9d9c-4b90-9cac-aafb1e38e82e", | ||
"scope": "openid", | ||
"id_token": "eyJjYWxsYmFja191cmwiOiJodHRwczovL2phbnMubG9jYWxob3N0L2phbnMtYXV0aC9yZXN0djEvYXV0aG9yaXplP3Jlc3BvbnNlX3R5cGU9Y29kZSZzZXNzaW9uX2lkPWI1MDFmNzg0LTY0MTAtNDIyYy1iYWQ0LWU0MTNiYTViMjI1NSZyZWRpcmVjdF91cmk9aHR0cHMlM0ElMkYlMkZqYW5zLmxvY2FsaG9zdCUyRmphbnMtYXV0aC1ycCUyRmhvbWUuaHRtJmNsaWVudF9pZD0xNGUzNmUxOC0xZDUxLTQxYWMtYTRjZi1hN2RjNjc3ZjUzYTUiLCJraWQiOiIxNmEyMmIwMy03YjUzLTQxY2QtYWE3OS01YjI1ZjNlY2QzOGNfc2lnX3JzMjU2IiwidHlwIjoiand0IiwiYWxnIjoiUlMyNTYifQ.eyJjYWxsYmFja191cmwiOiJodHRwczovL2phbnMubG9jYWxob3N0L2phbnMtYXV0aC9yZXN0djEvYXV0aG9yaXplP3Jlc3BvbnNlX3R5cGU9Y29kZSZzZXNzaW9uX2lkPWI1MDFmNzg0LTY0MTAtNDIyYy1iYWQ0LWU0MTNiYTViMjI1NSZyZWRpcmVjdF91cmk9aHR0cHMlM0ElMkYlMkZqYW5zLmxvY2FsaG9zdCUyRmphbnMtYXV0aC1ycCUyRmhvbWUuaHRtJmNsaWVudF9pZD0xNGUzNmUxOC0xZDUxLTQxYWMtYTRjZi1hN2RjNjc3ZjUzYTUiLCJhdWQiOiIxNGUzNmUxOC0xZDUxLTQxYWMtYTRjZi1hN2RjNjc3ZjUzYTUiLCJhY3IiOiJzaW1wbGVfcGFzc3dvcmRfYXV0aCIsInN1YiI6InpMM3FrWFVwZE9OOXoxRDNNVUkyRG1CTHd3MzA1RUt4eWY3al8zb0oyaDQiLCJjb2RlIjoiNzg4OTA4ZTAtMmM3MC00YjE1LWFkZmUtYWZlMDBiMTgyMTkxIiwiYW1yIjpbXSwiaXNzIjoiaHR0cHM6Ly9qYW5zLmxvY2FsaG9zdCIsImV4cCI6MTY1OTc1ODA4MywiZ3JhbnQiOiJwYXNzd29yZCIsImlhdCI6MTY1OTc1NDQ4Mywic2lkIjoiODA0OGZjYmYtM2RjNC00YjFhLTgwYjktNmU1NTkzZTJhOWMwIiwib3hPcGVuSURDb25uZWN0VmVyc2lvbiI6Im9wZW5pZGNvbm5lY3QtMS4wIn0.u0sK9Yccf-P0OLQe0cKz75dF3cKQzQt9QpoHbqBsuIgXb4YwzDTWSuVM055USo5iO9P3KUiIGnMs6WPrZa-xcH84oKWsjF4TnAHdtwE_1xSofxDUU1nSdg8V3mRjHmZKWAvSmGtWTJFqTNGJKQsz1xrTpZ4ZAsI8Ey5OUNIhLsS6BIrFixLm3Sjufe4I2hiTtUPXb1PSYkPgHPZI00h8h4NDspPM9syufMbSLi-HN1aTQaJCONw2X-4KnvLGq1utX7qryclW1pq17W2j6Z49CjCW5ZtstK2p2FRoPwnInXNNr6vqSrpWR6pY9Uus7LHZZbg2DWKBASyfdjN6ecQaIw", | ||
"token_type": "Bearer", | ||
"expires_in": 299 | ||
} | ||
``` | ||
|
||
- ### Step 3: callback_uri (/authorize) | ||
|
||
Request: | ||
``` | ||
curl --location --request GET 'https://{your-gluu-url}/oxauth/restv1/authorize?response_type=code&session_id=b501f784-6410-422c-bad4-e413ba5b2255&redirect_uri=https%3A%2F%2Fjans.localhost%2Fjans-auth-rp%2Fhome.htm&client_id=14e36e18-1d51-41ac-a4cf-a7dc677f53a5' | ||
``` | ||
|
||
Response: (return to the **redirect_uri**) | ||
``` | ||
https://jans.localhost/jans-auth-rp/home.htm?code=441688df-8f36-4e2c-8174-18d23cc88049&acr_values=pa-external-authn&session_id=7ee59d72-d59a-49ce-a0cb-19c4fcfc404c&session_state=c3f595a892208e3d237722ad06d830f199295ccc355827c436fff71509401eae.a505421b-a332-4604-8772-6ca345c4a4b9 | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
# PersonAuthentication External Authn | ||
|
||
from org.gluu.model.custom.script.type.auth import PersonAuthenticationType | ||
from org.gluu.service.cdi.util import CdiUtil | ||
from org.gluu.oxauth.security import Identity | ||
from org.gluu.oxauth.service import AuthenticationService | ||
from org.gluu.util import StringHelper | ||
from org.gluu.oxauth.util import ServerUtil | ||
from org.gluu.oxauth.service import SessionIdService | ||
from org.gluu.oxauth.service import CookieService | ||
from org.gluu.service.cache import CacheProvider | ||
from javax.faces.context import ExternalContext | ||
from java.util import HashMap | ||
from org.gluu.oxauth.service import UserService, RequestParameterService | ||
from org.gluu.oxauth.service.net import HttpService | ||
from javax.faces.context import FacesContext | ||
from org.gluu.jsf2.service import FacesService | ||
|
||
import java | ||
import uuid | ||
|
||
class PersonAuthentication(PersonAuthenticationType): | ||
def __init__(self, currentTimeMillis): | ||
self.currentTimeMillis = currentTimeMillis | ||
|
||
def init(self, customScript, configurationAttributes): | ||
print "PA External Authn. Initialization" | ||
print "PA External Authn. Initialized successfully configurationAttributes = %s" % configurationAttributes | ||
|
||
self.url_step1 = None | ||
|
||
# Get Custom Properties | ||
try: | ||
self.url_step1 = configurationAttributes.get("urlstep1").getValue2() | ||
print "PA External Authn. Initialization. url_step1: '%s'" % self.url_step1 | ||
except: | ||
print 'Missing required configuration attribute "urlstep1"' | ||
|
||
return True | ||
|
||
def destroy(self, configurationAttributes): | ||
print "PA External Authn. Destroy" | ||
print "PA External Authn. Destroyed successfully" | ||
return True | ||
|
||
def getAuthenticationMethodClaims(self, requestParameters): | ||
return None | ||
|
||
def getApiVersion(self): | ||
return 11 | ||
|
||
def isValidAuthenticationMethod(self, usageType, configurationAttributes): | ||
return True | ||
|
||
def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes): | ||
return None | ||
|
||
def authenticate(self, configurationAttributes, requestParameters, step): | ||
print "PA External Authn. Authenticate for step: %s" % step | ||
return True | ||
|
||
def prepareForStep(self, configurationAttributes, requestParameters, step): | ||
if (step == 1): | ||
return True | ||
else: | ||
return False | ||
|
||
def getExtraParametersForStep(self, configurationAttributes, step): | ||
return None | ||
|
||
def getCountAuthenticationSteps(self, configurationAttributes): | ||
return 1 | ||
|
||
def getPageForStep(self, configurationAttributes, step): | ||
print "PA External Authn. GetPageForStep for step: %s" % step | ||
|
||
externalContext = CdiUtil.bean(ExternalContext) | ||
sessionId = ServerUtil.getFirstValue(externalContext.getRequestParameterValuesMap(), "session_id") | ||
if (sessionId == None): | ||
# Retrieve sessionId from request parameters and validate it | ||
redirectUri = ServerUtil.getFirstValue(externalContext.getRequestParameterValuesMap(), "redirect_uri") | ||
if (redirectUri == None or StringHelper.isEmpty(redirectUri)): | ||
print "PA External Authn. GetPageForStep redirect_uri is null or empty" | ||
return "" | ||
print "PA External Authn. GetPageForStep redirect_uri '%s' found in request parameters" % redirectUri | ||
|
||
clientId = ServerUtil.getFirstValue(externalContext.getRequestParameterValuesMap(), "client_id") | ||
if (clientId == None or StringHelper.isEmpty(clientId)): | ||
print "PA External Authn. GetPageForStep client_id is null or empty" | ||
return "" | ||
print "PA External Authn. GetPageForStep client_id '%s' found in request parameters" % clientId | ||
|
||
# Generate jansKey | ||
jansKey = str(uuid.uuid4()); | ||
print "PA External Authn. GetPageForStep jansKey '%s' generated" % jansKey | ||
|
||
# Create JSON Values | ||
jsonValues = {} | ||
jsonValues["redirectUri"] = str(redirectUri) | ||
jsonValues["clientId"] = str(clientId) | ||
|
||
cacheProvider = CdiUtil.bean(CacheProvider) | ||
cacheProvider.put(300, jansKey, jsonValues) | ||
print "PA External Authn. GetPageForStep jansKey '%s' added to cache: %s" % (jansKey, jsonValues) | ||
|
||
requestParameterService = CdiUtil.bean(RequestParameterService) | ||
parametersMap = HashMap() | ||
parametersMap.put("jansKey", jansKey) | ||
callBackUrl = requestParameterService.parametersAsString(parametersMap) | ||
callBackUrl = "%s?%s" % (self.url_step1, callBackUrl) | ||
|
||
print "PA External Authn. GetPageForStep redirect to %s" % callBackUrl | ||
|
||
facesService = CdiUtil.bean(FacesService) | ||
facesService.redirectToExternalURL(callBackUrl) | ||
|
||
return "" | ||
|
||
return "postlogin.xhtml" | ||
|
||
def getNextStep(self, configurationAttributes, requestParameters, step): | ||
return -1 | ||
|
||
def getLogoutExternalUrl(self, configurationAttributes, requestParameters): | ||
return None | ||
|
||
def logout(self, configurationAttributes, requestParameters): | ||
return True |
Oops, something went wrong.