# HG changeset patch # User paulb # Date 1195259416 0 # Node ID 5b68f06b8d2461fb149f44f79d8011020ddd552c # Parent a7072c65117074d2d20df185be91a39e197e95ee [project @ 2007-11-17 00:30:16 by paulb] Added a POST-based form to the success messages which permits the "HTML FORM Redirection" mode of indirect communication described in the "OpenID 2.0 Authentication" draft. diff -r a7072c651170 -r 5b68f06b8d24 WebStack/Resources/OpenIDInitiation.py --- a/WebStack/Resources/OpenIDInitiation.py Tue Nov 13 23:17:11 2007 +0000 +++ b/WebStack/Resources/OpenIDInitiation.py Sat Nov 17 00:30:16 2007 +0000 @@ -38,9 +38,9 @@ The optional 'openid_mode' parameter may be set to "checkid_immediate" or "checkid_setup" (the default). - If the optional 'use_redirect' flag is set to 0, a confirmation screen - is given instead of redirecting the user back to the original - application. + If the optional 'use_redirect' flag is set to a false value (which is + not the default), a confirmation screen is given instead of immediately + redirecting the user to the OpenID provider. The optional 'urlencoding' parameter allows a special encoding to be used in producing the redirection path. @@ -101,11 +101,11 @@ ) # Show the success page anyway. + # Offer a POST-based form for redirection. - self.show_success(trans, url) + self.show_success(trans, provider, app, claimed_identifier, local_identifier) # Redirect to the OpenID provider URL. - # NOTE: Offer a POST-based form for redirection. if self.use_redirect: trans.redirect(url) @@ -157,16 +157,19 @@ out = trans.get_response_stream() out.write(self.initiation_page % app) - def show_success(self, trans, url): + def show_success(self, trans, provider, app, claimed_identifier, local_identifier): """ Writes a success screen using the transaction 'trans', including details - of the OpenID provider 'url'. + of the OpenID 'provider', the 'app' URL, 'claimed_identifier' and + 'local_identifier'. """ trans.set_content_type(WebStack.Generic.ContentType("text/html", self.encoding)) out = trans.get_response_stream() - out.write(self.success_page % (url, url)) + out.write(self.success_page % ( + provider, self.openid_ns, self.openid_mode, app, claimed_identifier, local_identifier) + ) initiation_page = """ @@ -177,8 +180,8 @@

Authenticate via OpenID

OpenID Identifier (URL):

-

- +

+
@@ -191,7 +194,14 @@

Authenticate via OpenID

-

Please proceed to the OpenID provider: %s.

+
+ + + + + +

Please proceed to the OpenID provider:

+
""" diff -r a7072c651170 -r 5b68f06b8d24 WebStack/Resources/OpenIDLogin.py --- a/WebStack/Resources/OpenIDLogin.py Tue Nov 13 23:17:11 2007 +0000 +++ b/WebStack/Resources/OpenIDLogin.py Sat Nov 17 00:30:16 2007 +0000 @@ -43,9 +43,9 @@ The optional 'associations' is a mapping from association handles to secret keys. - If the optional 'use_redirect' flag is set to 0, a confirmation screen - is given instead of redirecting the user back to the original - application. + If the optional 'use_redirect' flag is set to a false value (which is + not the default), a confirmation screen is given instead of immediately + redirecting the user back to the original application. The optional 'urlencoding' parameter allows a special encoding to be used in producing the redirection path. @@ -71,18 +71,18 @@ # Check for a submitted login form. - fields_body = trans.get_fields_from_body(self.encoding) + fields = trans.get_fields(self.encoding) - if fields_body.has_key("login"): + if fields.has_key("login"): # Check a combination of local identifier and username together with # the password. - claimed_id = fields_body.get("claimed_id", [""])[0] - local_id = fields_body.get("local_id", [""])[0] - username = fields_body.get("username", [""])[0] - password = fields_body.get("password", [""])[0] - app = fields_body.get("app", [""])[0] + claimed_id = fields.get("claimed_id", [""])[0] + local_id = fields.get("local_id", [""])[0] + username = fields.get("username", [""])[0] + password = fields.get("password", [""])[0] + app = fields.get("app", [""])[0] # NOTE: Permit flexibility in the credentials. @@ -92,13 +92,13 @@ # Check for an OpenID signature verification request. - elif fields_body.get("openid.mode", [None])[0] == "check_authentication": + elif fields.get("openid.mode", [None])[0] == "check_authentication": # Obtain the secret key from recorded associations. - handle = fields_body.get("openid.assoc_handle", [None])[0] + handle = fields.get("openid.assoc_handle", [None])[0] if handle is not None and self.associations.has_key(handle): - valid = check_openid_signature(fields_body, self.associations[handle]) + valid = check_openid_signature(fields, self.associations[handle]) del self.associations[handle] else: valid = 0 @@ -112,10 +112,9 @@ # Otherwise, show the login form. - fields_path = trans.get_fields_from_path(self.urlencoding) - app = fields_path.get("openid.return_to", [""])[0] - claimed_id = fields_path.get("openid.claimed_id", [""])[0] - local_id = fields_path.get("openid.identity", [""])[0] + app = fields.get("openid.return_to", [""])[0] + claimed_id = fields.get("openid.claimed_id", [""])[0] + local_id = fields.get("openid.identity", [""])[0] self.show_login(trans, app, claimed_id, local_id) @@ -168,9 +167,9 @@ url += "&%s=%s" % (name, trans.encode_path(value[0], self.urlencoding)) # Show the success page anyway. - # NOTE: Offer a POST-based form for redirection. + # Offer a POST-based form for redirection. - self.show_success(trans, url) + self.show_success(trans, app, "id_res", signed_names, signature, fields) if self.use_redirect: trans.redirect(url) else: @@ -207,16 +206,22 @@ out = trans.get_response_stream() out.write(self.login_page % (app, claimed_id, local_id)) - def show_success(self, trans, app): + def show_success(self, trans, app, mode, signed_names, signature, fields): """ Writes a success screen using the transaction 'trans', including details - of the 'app' which the client was attempting to access. + of the 'app' which the client was attempting to access, the + communications 'mode', the 'signed_names' indicating the fields which + are signed, the 'signature' associated with the message, and a + dictionary of 'fields' indicating other information. """ trans.set_content_type(WebStack.Generic.ContentType("text/html", self.encoding)) out = trans.get_response_stream() - out.write(self.success_page % (app, app)) + l = [] + for name, value in fields.items(): + l.append("""""" % (name, value[0])) + out.write(self.success_page % (app, self.openid_ns, mode, ",".join(signed_names), signature, "\n".join(l))) login_page = """ @@ -244,7 +249,14 @@

Login Successful

-

Please proceed to the application: %s

+
+ + + + + %s +

Please proceed to the application:

+
""" diff -r a7072c651170 -r 5b68f06b8d24 WebStack/Resources/OpenIDRedirect.py --- a/WebStack/Resources/OpenIDRedirect.py Tue Nov 13 23:17:11 2007 +0000 +++ b/WebStack/Resources/OpenIDRedirect.py Sat Nov 17 00:30:16 2007 +0000 @@ -37,16 +37,16 @@ "Respond using the given transaction 'trans'." - fields_path = trans.get_fields_from_path(self.path_encoding) + fields = trans.get_fields(self.path_encoding) # If requested, attempt to verify OpenID assertions. # http://openid.net/specs/openid-authentication-2_0-12.html#rfc.section.11 - if fields_path.get("openid.ns", [None])[0] == self.openid_ns and \ - fields_path.get("openid.mode", [None])[0] == "id_res": + if fields.get("openid.ns", [None])[0] == self.openid_ns and \ + fields.get("openid.mode", [None])[0] == "id_res": if self.authenticator.authenticate(trans, verify=1): - trans.redirect(fields_path["openid.return_to"][0]) + trans.redirect(fields["openid.return_to"][0]) # Otherwise, handle the usual parameters and request details. @@ -92,18 +92,18 @@ # If requested, attempt to verify OpenID assertions. if verify: - fields_path = trans.get_fields_from_path(self.urlencoding) + fields = trans.get_fields(self.urlencoding) # NOTE: Could expose all errors. try: # Test the details of the assertion. - if self.test_url(fields_path) and \ - self.test_signature(fields_path) and \ - self.test_replay(fields_path): + if self.test_url(fields) and \ + self.test_signature(fields) and \ + self.test_replay(fields): - self.set_token(trans, fields_path["openid.identity"][0]) + self.set_token(trans, fields["openid.identity"][0]) return 1 # Incomplete assertion. @@ -127,7 +127,7 @@ trans.set_user(username) return valid - def test_url(self, fields_path): + def test_url(self, fields): """ See: @@ -136,9 +136,9 @@ # NOTE: Currently, this is not strict enough. - return fields_path["openid.return_to"][0].startswith(self.app_url) + return fields["openid.return_to"][0].startswith(self.app_url) - def test_signature(self, fields_path): + def test_signature(self, fields): """ See: @@ -146,7 +146,7 @@ http://openid.net/specs/openid-authentication-2_0-12.html#rfc.section.6 """ - handle = fields_path.get("openid.assoc_handle", [None])[0] + handle = fields.get("openid.assoc_handle", [None])[0] # With an association handle, look up the appropriate secret key and # verify the signed items. @@ -156,19 +156,19 @@ # Where an association exists, use the known secret key. if self.associations.has_key(handle): - return check_openid_signature(fields_path, self.associations[handle]) + return check_openid_signature(fields, self.associations[handle]) # Without an association, request verification of the signed items # from the OpenID provider. else: - return self.test_signature_direct(fields_path) + return self.test_signature_direct(fields) # Without a handle, no signature verification can occur. return 0 - def test_signature_direct(self, fields_path): + def test_signature_direct(self, fields): """ See: @@ -178,7 +178,7 @@ # Make a POST request using the "openid." fields. d = {} - for name, values in fields_path.items(): + for name, values in fields.items(): if name.startswith("openid.") and name != "openid.mode": d[name] = values[0] d["openid.mode"] = "check_authentication" @@ -187,7 +187,7 @@ # Send a POST request to the OpenID provider, reading the response and # testing for certain fields and values. - f = urllib.urlopen(fields_path["openid.op_endpoint"][0], data) + f = urllib.urlopen(fields["openid.op_endpoint"][0], data) try: items = [] for line in f.readlines(): @@ -200,14 +200,14 @@ finally: f.close() - def test_replay(self, fields_path): + def test_replay(self, fields): """ See: http://openid.net/specs/openid-authentication-2_0-12.html#rfc.section.11.3 """ - timestamp = fields_path["openid.response_nonce"][0] + timestamp = fields["openid.response_nonce"][0] # YYYY-MM-DDTHH:MM:SSZ... year, month, day, hour, minute, second, unique = \ int(timestamp[0:4]), int(timestamp[5:7]), int(timestamp[8:10]), \