forked from docs/doc-exports
Reviewed-by: Kabai, Zoltán Gábor <zoltan-gabor.kabai@t-systems.com> Co-authored-by: Wei, Hongmin <weihongmin1@huawei.com> Co-committed-by: Wei, Hongmin <weihongmin1@huawei.com>
248 lines
16 KiB
HTML
248 lines
16 KiB
HTML
<a name="iam_02_0002"></a><a name="iam_02_0002"></a>
|
|
|
|
<h1 class="topictitle1">IdP Initiated</h1>
|
|
<div id="body1529043542435"><p id="iam_02_0002__p41447184161156">This section uses the <strong id="iam_02_0002__b842352706215651">Client4ShibbolethIdP</strong> script as an example to describe how to obtain a federated authentication token in the IdP-initiated mode. The <strong id="iam_02_0002__b84235270610255">Client4ShibbolethIdP</strong> script simulates a user who logs in to the enterprise IdP using a browser. Therefore, by comparing the form data submitted by the browser and the client implementation data, this section helps users develop the client scripts of their enterprise IdP.</p>
|
|
<div class="section" id="iam_02_0002__section48708258144634"><h4 class="sectiontitle">Prerequisites</h4><ul id="iam_02_0002__ul1005986216625"><li id="iam_02_0002__li5202628916625">IdP-initiated federated identity authentication is supported by the enterprise IdP server.</li><li id="iam_02_0002__li5823594616752">The <strong id="iam_02_0002__b842352706103648">beautifulsoup4</strong> package of the Python module has been installed on the client.</li></ul>
|
|
</div>
|
|
<div class="section" id="iam_02_0002__section564187174136"><h4 class="sectiontitle">Flowchart</h4><p id="iam_02_0002__p131582331551">The following figure shows the IdP-initiated federation authentication process.</p>
|
|
<div class="fignone" id="iam_02_0002__fig563751422"><span class="figcap"><b>Figure 1 </b>Flowchart (IdP-initiated)</span><br><span><img class="imgResize" id="iam_02_0002__image1736214182187" src="en-us_image_0152520823.png" width="523.6875" height="148.980083" title="Click to enlarge"></span></div>
|
|
</div>
|
|
<div class="section" id="iam_02_0002__section13188540101541"><h4 class="sectiontitle">Description</h4><ol id="iam_02_0002__ol37501317101615"><li id="iam_02_0002__li1967539101615">The client calls the login link provided by IdP based on the IdP-initiated mode and sets the cloud platform address in the login link, that is, <strong id="iam_02_0002__b84235270684433">entityID</strong> in the metadata file of the cloud platform.</li><li id="iam_02_0002__li17707855101615">The client obtains the login page of the IdP. Users submit identity information to IdP for authentication through the client.</li><li id="iam_02_0002__li51557777115127">After users pass the authentication, IdP constructs an assertion carrying the user identity information and sends the SAML response. The response passes through the client.</li><li id="iam_02_0002__li39041810115153">The client encapsulates the SAML response, forwards the SAML response, and calls the API (federated token obtained in the IdP-initiated mode) provided by the cloud platform.</li><li id="iam_02_0002__li15831972115153">The cloud platform verifies and authenticates the assertion, and generates a temporary access credential according to the identity conversion rule configured by users in the identity provider.</li><li id="iam_02_0002__li8270023115153">Users can access cloud resources according to their permissions.</li></ol>
|
|
</div>
|
|
<div class="section" id="iam_02_0002__section27488823174127"><h4 class="sectiontitle">Implementation on the Client</h4><p id="iam_02_0002__p49521074162136">Download the <strong id="iam_02_0002__b14951126157">Client4ShibbolethIdP.py</strong> script (for reference only) from the following website to implement the federated identity authentication script from the enterprise IdP to the API/CLI side of the cloud system:</p>
|
|
<p id="iam_02_0002__p55297856162510"><a href="https://obs-iam-download.obs.eu-de.otc.t-systems.com/non-ecp-script/Client4ShibblethIdP.py" target="_blank" rel="noopener noreferrer">https://obs-iam-download.obs.eu-de.otc.t-systems.com/non-ecp-script/Client4ShibblethIdP.py</a></p>
|
|
<ol id="iam_02_0002__ol26022968174434"><li id="iam_02_0002__li51972198174434"><span>Configure the login URL of enterprise IdP.</span><p>
|
|
<div class="tablenoborder"><table cellpadding="4" cellspacing="0" summary="" id="iam_02_0002__table1562155513354" frame="border" border="1" rules="all"><caption><b>Table 1 </b>Login URLs of common IdP products</caption><thead align="left"><tr id="iam_02_0002__row0561115523519"><th align="left" class="cellrowborder" valign="top" width="23.18%" id="mcps1.3.5.4.1.2.1.2.4.1.1"><p id="iam_02_0002__p35611555133516">IdP</p>
|
|
</th>
|
|
<th align="left" class="cellrowborder" valign="top" width="27.32%" id="mcps1.3.5.4.1.2.1.2.4.1.2"><p id="iam_02_0002__p20561155513518">SP Identification Parameter in URL</p>
|
|
</th>
|
|
<th align="left" class="cellrowborder" valign="top" width="49.5%" id="mcps1.3.5.4.1.2.1.2.4.1.3"><p id="iam_02_0002__p45612550357">Login URL Example</p>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody><tr id="iam_02_0002__row205612552353"><td class="cellrowborder" valign="top" width="23.18%" headers="mcps1.3.5.4.1.2.1.2.4.1.1 "><p id="iam_02_0002__p65611755113514">ADFS</p>
|
|
</td>
|
|
<td class="cellrowborder" valign="top" width="27.32%" headers="mcps1.3.5.4.1.2.1.2.4.1.2 "><p id="iam_02_0002__p1056115517354">logintorp</p>
|
|
</td>
|
|
<td class="cellrowborder" valign="top" width="49.5%" headers="mcps1.3.5.4.1.2.1.2.4.1.3 "><p id="iam_02_0002__p756165573519">https://adfs-server.contoso.com/adfs/ls/IdpInitiatedSignon.aspx?logintorp=https://iam.example.com</p>
|
|
</td>
|
|
</tr>
|
|
<tr id="iam_02_0002__row45622055143512"><td class="cellrowborder" valign="top" width="23.18%" headers="mcps1.3.5.4.1.2.1.2.4.1.1 "><p id="iam_02_0002__p956175519358">Shibboleth</p>
|
|
</td>
|
|
<td class="cellrowborder" valign="top" width="27.32%" headers="mcps1.3.5.4.1.2.1.2.4.1.2 "><p id="iam_02_0002__p1656175563510">providerId</p>
|
|
</td>
|
|
<td class="cellrowborder" valign="top" width="49.5%" headers="mcps1.3.5.4.1.2.1.2.4.1.3 "><p id="iam_02_0002__p656215553511">https://idp.example.org/idp/profile/SAML2/Unsolicited/SSO?providerId=iam.example.com</p>
|
|
</td>
|
|
</tr>
|
|
<tr id="iam_02_0002__row1356245553520"><td class="cellrowborder" valign="top" width="23.18%" headers="mcps1.3.5.4.1.2.1.2.4.1.1 "><p id="iam_02_0002__p7562125553518">SimpleSAMLphp</p>
|
|
</td>
|
|
<td class="cellrowborder" valign="top" width="27.32%" headers="mcps1.3.5.4.1.2.1.2.4.1.2 "><p id="iam_02_0002__p11562105553512">spentityid</p>
|
|
</td>
|
|
<td class="cellrowborder" valign="top" width="49.5%" headers="mcps1.3.5.4.1.2.1.2.4.1.3 "><p id="iam_02_0002__p1356213553350">https://idp.example.org/simplesaml/saml2/idp/SSOService.php?spentityid=iam.example.com</p>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p id="iam_02_0002__p65627558352">After the configuration, enter the login URL in the browser address box. The following page is displayed.</p>
|
|
<div class="fignone" id="iam_02_0002__fig256285516351"><span class="figcap"><b>Figure 2 </b>Login Page</span><br><span><img id="iam_02_0002__image16562135583517" src="en-us_image_0147658878.jpg" height="324.786" width="372.36675" title="Click to enlarge" class="imgResize"></span></div>
|
|
<div class="p" id="iam_02_0002__p85635557350">Client4ShibbolethIdP script implementation:<pre class="screen" id="iam_02_0002__screen20563145519355">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</pre>
|
|
</div>
|
|
</p></li></ol><ol start="2" id="iam_02_0002__ol154686174955"><li id="iam_02_0002__li17790161174514"><span>The client submits authentication information. The client parses the login page using the beautifulsoup4 module, captures the user information input box and requested action, constructs the request parameters, and initiates identity authentication to the IdP.</span><p><p id="iam_02_0002__p20581925181534">Obtain all form data submitted for the login page from the browser.</p>
|
|
<div class="fignone" id="iam_02_0002__fig1887664312288"><span class="figcap"><b>Figure 3 </b>Authentication information (1)</span><br><span><img id="iam_02_0002__image8876124332815" src="en-us_image_0151316952.png" height="211.47" width="523.6875" title="Click to enlarge" class="imgResize"></span></div>
|
|
<p id="iam_02_0002__p18996147112913"></p>
|
|
<p id="iam_02_0002__p66792027181818">Client4ShibbolethIdP script implementation:</p>
|
|
<pre class="screen" id="iam_02_0002__screen16630181181948"># 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)</pre>
|
|
</p></li><li id="iam_02_0002__li46150719181440"><span>The client parses the next page. (Some enterprise IdPs provide pages containing user attributes.)</span><p><p id="iam_02_0002__p17485353182549">Obtain all form data submitted for the login page from the browser.</p>
|
|
<div class="fignone" id="iam_02_0002__fig179001019556"><span class="figcap"><b>Figure 4 </b>Authentication information (2)</span><br><span><img id="iam_02_0002__image354468518283" src="en-us_image_0147658838.jpg" height="265.3325461254615" width="494.76000000000005" title="Click to enlarge" class="imgResize"></span></div>
|
|
<p id="iam_02_0002__p61044429182816">Client4ShibbolethIdP script implementation:</p>
|
|
<pre class="screen" id="iam_02_0002__screen54047903182847"># 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)</pre>
|
|
</p></li><li id="iam_02_0002__li1595859182451"><span>The client parses the response message sent from the IdP. The client submits user information to the enterprise IdP for authentication. After authenticating the user information, the IdP sends a response message to the client. The client parses the <strong id="iam_02_0002__b84235270613010">SAMLResponse</strong> parameter in the response message.</span><p><p id="iam_02_0002__p2630481118337">Client4ShibbolethIdP script implementation:</p>
|
|
<pre class="screen" id="iam_02_0002__screen38407753183348"># 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)</pre>
|
|
</p></li><li id="iam_02_0002__li1540181518304"><span>Obtain an unscoped token. For details, see <a href="iam_02_0003.html">Obtaining an Unscoped Token (IdP Initiated)</a>.</span><p><p id="iam_02_0002__p11198954183647">Client4ShibbolethIdP script implementation:</p>
|
|
<pre class="screen" id="iam_02_0002__screen31405262183712"># 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</pre>
|
|
</p></li><li id="iam_02_0002__li43207489183528"><span>Obtain a scoped token. For details, see <a href="iam_13_0604.html">Obtaining a Scoped Token</a>.</span><p><p id="iam_02_0002__p25545397183917">Client4ShibbolethIdP script implementation:</p>
|
|
<pre class="screen" id="iam_02_0002__screen54563560184010">payload = {
|
|
"auth": {
|
|
"identity": {
|
|
"methods": ["token"],
|
|
"token": {
|
|
"id": unscoped_token
|
|
}
|
|
},
|
|
"scope": {
|
|
"project": {
|
|
"name": "{region_id}_test1"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sp_scoped_token_url = "https://iam.example.com/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</pre>
|
|
</p></li><li id="iam_02_0002__li4627979918391"><span>Obtain a temporary AK/SK. For details, see <a href="en-us_topic_0097949518.html">Obtaining a Temporary AK/SK</a>.</span><p><p id="iam_02_0002__p44972820184120">Client4ShibbolethIdP script implementation:</p>
|
|
<pre class="screen" id="iam_02_0002__screen700567184222"># 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://iam.example.com/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</pre>
|
|
</p></li></ol>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="familylinks">
|
|
<div class="parentlink"><strong>Parent topic:</strong> <a href="en-us_topic_0057845646.html">Obtaining a Token in Federated Identity Authentication Mode</a></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<script language="JavaScript">
|
|
<!--
|
|
image_size('.imgResize');
|
|
var msg_imageMax = "view original image";
|
|
var msg_imageClose = "close";
|
|
//--></script> |