IdP iniciado
Esta sección utiliza el script Client4ShibbolethIdP como ejemplo para describir cómo obtener un token de autenticación federado en el modo iniciado por IdP. El script Client4ShibbolethIdP simula un usuario que inicia sesión en un sistema IdP usando un navegador, lo que le ayuda a desarrollar su propio script de cliente IdP.
Prerrequisitos
- Su servidor IdP admite la autenticación de identidad federada iniciada por IdP.
- La biblioteca de Python BeautifulSoup 4 se ha instalado en el cliente.
Diagrama de flujo
La siguiente figura muestra el proceso de autenticación de federación iniciada por IdP.
Descripción
- El cliente visita el enlace de inicio de sesión proporcionado por IdP para el inicio de sesión iniciado por IdP y establece la dirección de nube pública (entityID en el archivo de metadatos del sistema en la nube) en el enlace de inicio de sesión.
- El cliente muestra la página de inicio de sesión de IdP, lo que permite a los usuarios enviar información de identidad a IdP para su autenticación.
- Después de autenticar al usuario, el IdP construye una aserción que lleva la información de identidad del usuario y envía una respuesta SAML al cliente.
- El cliente encapsula la respuesta SAML y la reenvía al sistema en la nube para invocar a la API utilizada para obtener un token federado en el modo iniciado por IdP.
- El sistema en la nube verifica y autentica la aserción, y genera una credencial de acceso temporal de acuerdo con las reglas de conversión de identidad del usuario configurado para el proveedor de identidad.
- El usuario puede acceder a los recursos de la nube pública en función de los permisos asignados.
Implementación en el cliente
Esta sección utiliza el script Client4ShibbolethIdP.py para describir cómo implementar la autenticación de identidad federada para el acceso de API/CLI al sistema en la nube desde un IdP.
Puede descargar el script aquí:
https://obs-iam-download01.obs.cn-north-1.myhwclouds.com/non-ecp-script/Client4ShibblethIdP.py
- Configure la URL de inicio de sesión del IdP.
Tabla 1 URL de inicio de sesión de productos comunes de IdP IdP
Parámetro de SP en URL
Ejemplo de URL de inicio de sesión
ADFS
logintorp
https://adfs-server.contoso.com/adfs/ls/IdpInitiatedSignon.aspx?logintorp=https://iam.example.com
Shibboleth
providerId
https://idp.example.org/idp/profile/SAML2/Unsolicited/SSO?providerId=iam.example.com
SimpleSAMLphp
spentityid
https://idp.example.org/simplesaml/saml2/idp/SSOService.php?spentityid=iam.example.com
Después de la configuración, introduzca una URL de inicio de sesión en la barra de direcciones de un navegador. Aparecerá en la pantalla la página siguiente.
Figura 2 Página de inicio de sesión
Script Client4ShibbolethIdP:import sys import requests import getpass import re from bs4 import BeautifulSoup from urlparse import urlparse # SSL certificate verification: Whether or not strict certificate # verification is done, False should only be used for dev/test sslverification = True # Get the federated credentials from the user print "Username:", username = raw_input() password = getpass.getpass() print '' session = requests.Session() # The initial url that starts the authentication process. idp_entry_url = 'https://idp.example.com/idp/profile/SAML2/Unsolicited/SSO?providerId=https://iam.example.com' # Programmatically get the SAML assertion,open the initial IdP url# and follows all of the HTTP302 redirects, and gets the resulting# login page formresponse = session.get(idp_entry_url, verify=sslverification) # Capture the idp_authform_submit_url,which is the final url after# all the 302s idp_authform_submit_url = formresponse.url
- El cliente usa Beautifulsoup 4 para capturar la información del usuario y la acción solicitada, y luego construye y envía una solicitud de autenticación de identidad al IdP.
El cliente adquiere todos los datos del formulario enviados a través de la página de inicio de sesión.
Figura 3 Información de autenticación (1)
Script Client4ShibbolethIdP:
# Parse the response and extract all the necessary values in order to build a dictionary of all of the form values the IdP expects formsoup = BeautifulSoup(formresponse.text.decode('utf8'), "lxml") payload = {} for inputtag in formsoup.find_all(re.compile('(INPUT|input)')): name = inputtag.get('name', '') value = inputtag.get('value', '') if "username" in name.lower(): payload[name] = username elif "password" in name.lower(): payload[name] = password else: payload[name] = value for inputtag in formsoup.find_all(re.compile('(FORM|form)')): action = inputtag.get('action') if action: parsedurl = urlparse(idp_entry_url) idp_authform_submit_url = parsedurl.scheme + "://" + parsedurl.netloc + action # please test on browser first, add other parameters in payload payload["_eventId_proceed"] = "" formresponse = session.post( idp_authform_submit_url, data=payload, verify=sslverification)
- El cliente analiza la página siguiente. (Algunas IdPs muestran una página de atributos de usuario.)
El cliente adquiere todos los datos del formulario enviados a través de la página de inicio de sesión.
Figura 4 Información de autenticación (2)
Script Client4ShibbolethIdP:
# In shebbleth IdP v3, browser will show attributes page for user,# so we need parse the page formsoup = BeautifulSoup(formresponse.text.decode('utf8'), "lxml") payload = {} # Add other form data required from browser to payload _shib_idp_consentIds = [] for inputtag in formsoup.find_all(re.compile('input')): name = inputtag.get("name") value = inputtag.get("value") if name == "_shib_idp_consentIds": _shib_idp_consentIds.append(value) payload["_shib_idp_consentIds"] = _shib_idp_consentIds payload["_shib_idp_consentOptions"] = "_shib_idp_rememberConsent" payload["_eventId_proceed"] = "Accept" # user can get the action url from the html file nexturl = "https://idp.example.com/idp/profile/SAML2/Unsolicited/SSO?execution=e1s2" for inputtag in formsoup.find_all(re.compile('(FORM|form)')): action = inputtag.get('action') if action: parsedurl = urlparse(idp_entry_url) nexturl = parsedurl.scheme + "://" + parsedurl.netloc + action response = session.post( nexturl, data=payload, verify=sslverification)
- Si la autenticación se realiza correctamente, el cliente analiza la respuesta SAML enviada por el IdP.
Script Client4ShibbolethIdP:
# Decode the response and extract the SAML assertion soup = BeautifulSoup(response.text.decode('utf8'), "lxml") SAMLResponse = '' # Look for the SAMLResponse attribute of the input tag for inputtag in soup.find_all('input'): if (inputtag.get('name') == 'SAMLResponse'): SAMLResponse = inputtag.get('value') # Better error handling is required for production use. if (SAMLResponse == ''): print 'Response did not contain a valid SAML assertion, please troubleshooting in Idp side.' sys.exit(0)
- Obtenga un token sin alcance. Para obtener más información, véase Obtención de un token sin alcance (IdP iniciado).
Script Client4ShibbolethIdP:
# Set headers headers = {} headers["X-Idp-Id"] = "test_local_idp" # IAM API url: get unscoped token on IDP initiated mode sp_unscoped_token_url = "https://iam.example.com/v3.0/OS-FEDERATION/tokens" # Set form data payload = {} payload["SAMLResponse"] = SAMLResponse response = session.post( sp_unscoped_token_url, data=payload, headers=headers, verify=sslverification) # Debug only print(response.text) print "Status Code: " + str(response.status_code) if response.status_code != 201: sys.exit(1) unscoped_token = response.headers.get("X-Subject-Token") if "X-Subject-Token" in response.headers.keys() else None if unscoped_token: print ">>>>>>X-Subject-Token: " + unscoped_token
- Obtener un token de alcance. Para obtener más información, véase Obtención de un token con ámbito.
Client4ShibbolethIdP script:
payload = { "auth": { "identity": { "methods": ["token"], "token": { "id": unscoped_token } }, "scope": { "project": { "name": "{region_id}_test1" } } } } sp_scoped_token_url = "https://10.120.171.90:31943/v3/auth/tokens" response = session.post( sp_scoped_token_url, json=payload, verify=sslverification) # Debug only print "Status Code: " + str(response.status_code) if response.status_code != 201: print response.text sys.exit(1) scoped_token = response.text if response.status_code == 201 else None if scoped_token: print ">>>>>>Scoped Token:" + scoped_token
- Obtener una clave de acceso temporal. Para obtener más información, véase Obtención de una clave de acceso temporal y un token de seguridad a través de un token.
Script Client4ShibbolethIdP:
# Set form data payload = { "auth": { "identity": { "methods": ["token"], "token": { "duration_seconds": "900" } } } } # Set headers headers = {} headers["X-Auth-Token"] = unscoped_token sp_STS_token_url = "https://10.120.171.90:31943/v3.0/OS-CREDENTIAL/securitytokens" response = session.post( sp_STS_token_url, json=payload, headers=headers, verify=sslverification) # Debug only print "Status Code: " + str(response.status_code) if response.status_code != 201: print response.text sys.exit(1) sts_token = response.text if response.status_code == 201 else None if sts_token: print ">>>>>>STS Token:" + sts_token