def __call__(self, *args, **kwargs): log.debug("Start new authentication") # Redirect to the create challenge entry point if "key" in kwargs: return Redirect("{0}?key={1}".format(self.urls['url_createchallenge'],kwargs["key"])) else: return Redirect("{0}?".format(self.urls['url_createchallenge']))
def create_authn_request(self, session, acr_value=None, **kwargs): session["state"] = rndstr(32) request_args = { "response_type": self.behaviour["response_type"], "state": session["state"], "redirect_uri": self.registration_response["redirect_uris"][0] } try: request_args["scope"] = self.behaviour["scope"] except KeyError: pass request_args.update(kwargs) cis = self.construct_AuthorizationRequest(request_args=request_args) logger.debug("request: %s" % cis) url, body, ht_args, cis = self.uri_and_body(AuthorizationRequest, cis, method="GET", request_args=request_args) self.authz_req[request_args['state']] = cis logger.debug("body: %s" % body) logger.info("URL: %s" % url) logger.debug("ht_args: %s" % ht_args) resp = Redirect(str(url)) if ht_args: resp.headers.extend([(a, b) for a, b in ht_args.items()]) logger.debug("resp_headers: %s" % resp.headers) return resp
def authn_reply(self, areq, aresp, bsid, **kwargs): """ :param areq: Authorization Request :param aresp: Authorization Response :param bsid: Session id :param kwargs: Additional keyword args :return: """ if "redirect_uri" in areq: # TODO verify that the uri is reasonable redirect_uri = areq["redirect_uri"] else: redirect_uri = self.urlmap[areq["client_id"]] location = location_url(areq["response_type"], redirect_uri, aresp.to_urlencoded()) LOG_DEBUG("Redirected to: '%s' (%s)" % (location, type(location))) # set cookie containing session ID cookie = make_cookie(self.cookie_name, bsid, self.seed) return Redirect(str(location), headers=[cookie])
def create_authn_request(self, session, acr_value=None): session["state"] = rndstr() session["nonce"] = rndstr() request_args = { "response_type": self.behaviour["response_type"], "scope": self.behaviour["scope"], "state": session["state"], "nonce": session["nonce"], "redirect_uri": self.registration_response["redirect_uris"][0] } if acr_value is not None: request_args["acr_values"] = acr_value cis = self.construct_AuthorizationRequest(request_args=request_args) logger.debug("request: %s" % cis) url, body, ht_args, cis = self.uri_and_body(AuthorizationRequest, cis, method="GET", request_args=request_args) logger.debug("body: %s" % body) logger.info("URL: %s" % url) logger.debug("ht_args: %s" % ht_args) resp = Redirect(str(url)) if ht_args: resp.headers.extend([(a, b) for a, b in ht_args.items()]) logger.debug("resp_headers: %s" % resp.headers) return resp
def create_authn_request(self, session, acr_value=None, **kwargs): session["state"] = rndstr(32) request_args = { "response_type": self.behaviour["response_type"], "scope": self.behaviour["scope"], "state": session["state"], "redirect_uri": self.registration_response["redirect_uris"][0], } if self.oidc: session["nonce"] = rndstr(32) request_args["nonce"] = session["nonce"] if acr_value is not None: request_args["acr_values"] = acr_value request_args.update(kwargs) cis = self.construct_AuthorizationRequest(request_args=request_args) logger.debug("request: %s" % sanitize(cis)) url, body, ht_args, cis = self.uri_and_body( AuthorizationRequest, cis, method="GET", request_args=request_args ) self.authz_req[request_args["state"]] = cis logger.debug("body: %s" % sanitize(body)) logger.info("URL: %s" % sanitize(url)) logger.debug("ht_args: %s" % sanitize(ht_args)) resp = Redirect(str(url)) if ht_args: resp.headers.extend([(a, b) for a, b in ht_args.items()]) logger.debug("resp_headers: %s" % sanitize(resp.headers)) return resp
def logout(request): CLIENTS = request.environ["OIDC_CLIENTS"] client = CLIENTS[request.session["op"]] logout_url = client.endsession_endpoint try: # Specify to which URL the OP should return the user after # log out. That URL must be registered with the OP at client # registration. logout_url += "?" + urllib.urlencode({ "post_logout_redirect_uri": client.registration_response["post_logout_redirect_uris"][0] }) except KeyError: pass else: # If there is an ID token send it along as a id_token_hint _idtoken = get_id_token(client, request.session) if _idtoken: logout_url += "&" + urllib.urlencode({ "id_token_hint": id_token_as_signed_jwt(client, _idtoken, "HS256") }) request.session.clear() oic_resp = Redirect(str(logout_url)) oic_resp(request.environ, start_response) resp = HttpResponse(content_type=oic_resp._content_type, status=oic_resp._status) for key,val in oic_resp.headers: resp[key] = val return resp
def fini(self, session, conv): _tid = session["testid"] conv.test_output.append(("X", END_TAG)) self.store_test_info(session) self.dump_log(session, _tid) session["node"].complete = True _grp = _tid.split("-")[1] resp = Redirect("%sopresult#%s" % (self.conf.BASE, _grp)) return resp(self.environ, self.start_response)
def begin(self, environ, server_env, start_response, cookie, sid, info): state = rndstr() server_env["CACHE"].alternate_sid(sid, state) callback = server_env["base_url"] + self.social_endpoint # redirect the user to facebook for the authentication ar = AuthorizationRequest().from_dict({"client_id": self.client_id, "redirect_uri": callback, "state": state, "response_type": ["code"], "scope": self._scope}) url = ar.request(self.extra["authorization_endpoint"]) logger.info("[OAuth2] callback url: %s" % url) if cookie: resp = Redirect(url, headers=[cookie]) else: resp = Redirect(url) return resp(environ, start_response)
def run(self): _client = self.conv.entity url, body, ht_args, csi = _client.request_info( self.request, method=self.method, request_args=self.req_args, lax=True, **self.op_args) self.csi = csi self.conv.events.store(EV_REDIRECT_URL, url, sender=self.__class__.__name__) return Redirect(str(url))
def _redirect_authz_error(error, redirect_uri, descr=None, state="", return_type=None): err = AuthorizationErrorResponse(error=error) if descr: err["error_description"] = descr if state: err["state"] = state if return_type is None or return_type == ["code"]: location = err.request(redirect_uri) else: location = err.request(redirect_uri, True) return Redirect(location)
def verify(self, request, cookie, **kwargs): """ Verifies if the authentication was successful. :rtype : Response :param request: Contains the request parameters. :param cookie: Cookies sent with the request. :param kwargs: Any other parameters. :return: If the authentication was successful: a redirect to the return_to url. Otherwise a unauthorized response. :raise: ValueError """ logger.debug("verify(%s)" % request) if isinstance(request, six.string_types): _dict = urlparse.parse_qs(request) elif isinstance(request, dict): _dict = request else: raise ValueError("Wrong type of input") try: cas_cookie, _ts, _typ = self.getCookieValue( cookie, self.CONST_CAS_COOKIE) data = json.loads(cas_cookie) nonce = base64.b64decode(data[self.CONST_NONCE]) if nonce != _dict[self.CONST_NONCE][0]: logger.warning( 'Someone tried to login without a correct nonce!') return Unauthorized("You are not authorized!") acr = None try: acr = _dict["acr_values"][0] except KeyError: pass uid = self.handle_callback(_dict[self.CONST_TICKET], self.get_service_url(nonce, acr)) if uid is None or uid == "": logger.info('Someone tried to login, but was denied by CAS!') return Unauthorized("You are not authorized!") cookie = self.create_cookie(uid, "casm") return_to = self.generate_return_url(self.return_to, uid) if '?' in return_to: return_to += "&" else: return_to += "?" return_to += base64.b64decode(data[self.CONST_QUERY]) return Redirect(return_to, headers=[cookie]) except: logger.fatal('Metod verify in user_cas.py had a fatal exception.', exc_info=True) return Unauthorized("You are not authorized!")
def display_test_list(self, **kwargs): try: if self.sh.session_init(): return self.inut.flow_list() else: try: resp = Redirect("%s/opresult#%s" % (self.base_url, self.sh["testid"][0])) except KeyError: return self.inut.flow_list(**kwargs) else: return resp(self.inut.environ, self.inut.start_response) except Exception as err: exception_trace("display_test_list", err) return self.inut.err_response("session_setup", err)
def __call__(self, query, *args, **kwargs): """ Saves the query parameters sent from the client in a cookie and then redirects to the SPHandler. :param query: Query parameters to be returned to op server. :param args: Not used. :param kwargs: Not used. :return: """ if self.acr is not None and query is not None and query.find( Authenticate.CONST_ACR) == -1: query += "&" + Authenticate.CONST_ACR + "=" + self.acr cookie = self.create_cookie( '{"' + self.CONST_QUERY + '": "' + base64.b64encode(query) + '"}', self.CONST_SP_COOKIE, self.CONST_SP_COOKIE) return Redirect(self.redirect_url, headers=[cookie])
def application(environ, start_response): session = environ['beaker.session'] path = environ.get('PATH_INFO', '').lstrip('/') if path == "robots.txt": return static(environ, start_response, "static/robots.txt") if path.startswith("static/"): return static(environ, start_response, path) if path == "logout": session.invalidate() resp = Redirect("static/log_out_message.html") return resp(environ, start_response) if path == "as": session["callback"] = True request = parse_qs(get_or_post(environ)) _cli = CONSUMER[unquote(request["authzsrv"][0])] session["client"] = _cli resp = Redirect(_cli.begin(RP_CONF.BASE, path)) return resp(environ, start_response) if path == "authz_cb": _cli = session["client"] request = get_or_post(environ) aresp = _cli.handle_authorization_response(request) rargs = {"code": aresp["code"]} atresp = _cli.do_access_token_request(request_args=rargs) #extra_args=None, http_args=None,) # Access token should be stored somewhere for later usage Token[atresp["state"]] = atresp resp = Response("Got access token: %s" % atresp["access_token"]) return resp(environ, start_response) return as_choice(environ, start_response)
def display_test_list(self): try: if self.sh.session_init(): return self.io.flow_list(self.sh.session) else: try: resp = Redirect( "%sopresult#%s" % (self.io.conf.BASE, self.sh.session["testid"][0])) except KeyError: return self.io.flow_list(self.sh.session) else: return resp(self.io.environ, self.io.start_response) except Exception as err: exception_trace("display_test_list", err) return self.io.err_response(self.sh.session, "session_setup", err)
def run(self): _client = self.conv.entity _trace = self.conv.trace url, body, ht_args, csi = _client.request_info( self.request, method=self.method, request_args=self.req_args, lax=True, **self.op_args) self.csi = csi _trace.info("redirect.url: %s" % url) _trace.info("redirect.header: %s" % ht_args) self.conv.events.store('url', url) return Redirect(str(url))
def _create_authzreq(self, role, session, acr_value=""): request_args = role.get_request_args(acr_value, session) try: url, body, ht_args, csi = role.request_info( AuthorizationRequest, "GET", request_args=request_args) except Exception: message = traceback.format_exception(*sys.exc_info()) resp = ServiceError(message) else: resp_headers = [("Location", str(url))] if ht_args: resp_headers.extend([(a, b) for a, b in list(ht_args.items())]) resp = Redirect(url, headers=resp_headers) return resp
def test(environ, session, path): _test = path[5:] session["flow_index"] = FLOW_SEQUENCE.index(_test) session["phase_index"] = 0 session["start"] = 0 session["trace"] = Trace() _op = resp = client = link = None try: query = parse_qs(environ["QUERY_STRING"]) except KeyError: pass else: try: session["op"] = query["op"][0] try: assert session["op"] in SERVER_ENV["OP"] _op = session["op_conf"] = SERVER_ENV["OP"][session["op"]] except AssertionError: return BadRequest("OP chosen that is not configured") except KeyError: pass if _op: try: client = SERVER_ENV["OIC_CLIENT"][session["opkey"]] except KeyError: _key = rndstr() try: kwargs = {"deviate": _op["deviate"]} except KeyError: kwargs = {} client = create_client(_key, **kwargs) session["opkey"] = _key SERVER_ENV["OIC_CLIENT"][session["opkey"]] = client if _op["features"]["discovery"]: link = _op["provider"]["dynamic"] session["srv_discovery_url"] = link else: client.handle_provider_config(_op["provider"], session["op"]) else: resp = Redirect("/") if resp: return resp else: return {"client": client, "link": link}
def response(self, binding, http_args, query): cookie = self.create_cookie('{"' + self.CONST_QUERY + '": "' + base64.b64encode(query) + '" , "' + self.CONST_HASIDP + '": "True" }', self.CONST_SAML_COOKIE, self.CONST_SAML_COOKIE) if binding == BINDING_HTTP_ARTIFACT: resp = Redirect() elif binding == BINDING_HTTP_REDIRECT: for param, value in http_args["headers"]: if param == "Location": resp = SeeOther(str(value), headers=[cookie]) break else: raise ServiceErrorException("Parameter error") else: http_args["headers"].append(cookie) resp = Response(http_args["data"], headers=http_args["headers"]) return resp
def verify(self, request, **kwargs): """ Verifies that the given username and password was correct :param request: Either the query part of a URL a urlencoded body of a HTTP message or a parse such. :param kwargs: Catch whatever else is sent. :return: redirect back to where ever the base applications wants the user after authentication. """ logger.debug("verify(%s)" % request) if isinstance(request, basestring): _dict = parse_qs(request) elif isinstance(request, dict): _dict = request else: raise ValueError("Wrong type of input") logger.debug("dict: %s" % _dict) logger.debug("passwd: %s" % self.passwd) # verify username and password try: self._verify(_dict["password"][0], _dict["login"][0]) except (AssertionError, KeyError): resp = Unauthorized("Unknown user or wrong password") return resp, False else: # if "cookie" not in kwargs or self.srv.cookie_name not in kwargs["cookie"]: headers = [self.create_cookie(_dict["login"][0], "upm")] try: _qp = _dict["query"][0] except KeyError: _qp = self.get_multi_auth_cookie(kwargs['cookie']) try: return_to = self.generate_return_url(kwargs["return_to"], _qp) except KeyError: try: return_to = self.generate_return_url( self.return_to, _qp, kwargs["path"]) except KeyError: return_to = self.generate_return_url(self.return_to, _qp) return Redirect(return_to, headers=headers), True
def authz_part2(self, user, areq, sid, **kwargs): """ After the authentication this is where you should end up :param user: :param areq: The Authorization Request :param sid: Session key :param kwargs: possible other parameters :return: A redirect to the redirect_uri of the client """ result = self._complete_authz(user, areq, sid, **kwargs) if isinstance(result, Response): return result else: aresp, headers, redirect_uri, fragment_enc = result # Just do whatever is the default location = aresp.request(redirect_uri, fragment_enc) logger.debug("Redirected to: '%s' (%s)" % (location, type(location))) return Redirect(str(location), headers=headers)
def login_shi(): session["state"] = rndstr() session["nonce"] = rndstr() claims_request = ClaimsRequest( userinfo=Claims( uiucedu_uin={"essential": True} ) ) args = { "client_id": client.client_id, "response_type": "code", "scope": Config.SCOPES, "claims": claims_request, "nonce": session["nonce"], "redirect_uri": client.registration_response["redirect_uris"][0], "state": session["state"] } auth_req = client.construct_AuthorizationRequest(request_args=args) login_url = auth_req.request(client.authorization_endpoint) return Redirect(login_url)
def authn_redirect(self, uid, cookie): """ Creates the URL the SP should redirect to after a successful authentication. This is generally the clients request to the authorization endpoint at the OP. :param uid: Unique user identification a.k.a sub. :param cookie: Cookie string sent from the server. Same as environ["HTTP-COOKIE"] :return: A redirect URL. """ return_to = self.generateReturnUrl(self.return_to, uid) #Retrieve the query parameters saved by the method __call__ below. sp_cookie, _ts, _typ = self.getCookieValue(cookie, self.CONST_SP_COOKIE) data = json.loads(sp_cookie) if '?' in return_to: return_to += "&" else: return_to += "?" return_to += base64.b64decode(data[self.CONST_QUERY]) #Creates the cookie that the op server needs for authentication. return Redirect(return_to, headers=[self.create_cookie(uid, "spm")])
def verify(self, request, **kwargs): """ Verifies that the given username and password was correct :param request: Either the query part of a URL a urlencoded body of a HTTP message or a parse such. :param kwargs: Catch whatever else is sent. :return: redirect back to where ever the base applications wants the user after authentication. """ logger.debug("verify(%s)" % request) if isinstance(request, six.string_types): _dict = parse_qs(request) elif isinstance(request, dict): _dict = request else: raise ValueError("Wrong type of input") logger.debug("dict: %s" % _dict) logger.debug("passwd: %s" % self.passwd) # verify username and password try: assert _dict['login_parameter'][0] == 'logged_in' except (AssertionError, KeyError): resp = Unauthorized( "You are not authorized. Javascript not executed") return resp, False else: cookie = self.create_cookie("diana", "upm") try: _qp = _dict["query"][0] except KeyError: _qp = self.get_multi_auth_cookie(kwargs['cookie']) try: return_to = self.generate_return_url(kwargs["return_to"], _qp) except KeyError: return_to = self.generate_return_url(self.return_to, _qp) resp = Redirect(return_to, headers=[cookie]) return resp, True
def run(self): _client = self.conv.entity if 'add_state' in self.op_args: _state = rndstr(32) _client.logout_state2state[_state] = self.req_args['state'] self.op_args['state'] = self.req_args['state'] self.conv.end_session_state = _state self.req_args['state'] = _state logger.debug('req_args {}'.format(self.req_args)) logger.debug('op_args {}'.format(self.op_args)) url, body, ht_args, csi = _client.request_info( self.request, method=self.method, request_args=self.req_args, lax=True, **self.op_args) if 'remove_id_token_hint' in self.op_args: try: del csi['id_token_hint'] except KeyError: pass head, tail = url.split('?') _qs = parse_qs(tail) try: del _qs['id_token_hint'] except KeyError: pass _qs = {k: v[0] for k, v in _qs.items()} url = '{}?{}'.format(head, urlencode(_qs)) self.csi = csi self.conv.events.store(EV_REDIRECT_URL, url, sender=self.__class__.__name__) return Redirect(str(url))
def login(): session['state'] = rndstr() session['nonce'] = rndstr() # setup claim request claims_request = ClaimsRequest( userinfo = Claims(uiucedu_uin={"essential": True}) ) args = { "client_id": client.client_id, "response_type": "code", "scope": app.config["SCOPES"], "nonce": session["nonce"], "redirect_uri": app.config["REDIRECT_URIS"][0], "state": session["state"], "claims": claims_request } auth_req = client.construct_AuthorizationRequest(request_args=args) login_url = auth_req.request(client.authorization_endpoint) return Redirect(login_url)
def create_redirect(self, query): """ Performs the redirect to the CAS server. :rtype : Response :param query: All query parameters to be added to the return_to URL after successful authentication. :return: A redirect response to the CAS server. """ try: req = urlparse.parse_qs(query) acr = req['acr_values'][0] except KeyError: acr = None nonce = uuid.uuid4().get_urn() service_url = urlparse.urlencode( {self.CONST_SERVICE: self.get_service_url(nonce, acr)}) cas_url = self.cas_server + self.CONST_CASLOGIN + service_url cookie = self.create_cookie( '{"' + self.CONST_NONCE + '": "' + base64.b64encode(nonce) + '", "' + self.CONST_QUERY + '": "' + base64.b64encode(query) + '"}', self.CONST_CAS_COOKIE, self.CONST_CAS_COOKIE) return Redirect(cas_url, headers=[cookie])
if not allowed: return Unauthorized(self.not_authorized) #logger.info("parsed OK")' uid = response.assertion.subject.name_id.text self.setup_userdb(uid, response.ava) return_to = create_return_url(self.return_to, uid, **{self.query_param: "true"}) if '?' in return_to: return_to += "&" else: return_to += "?" return_to += base64.b64decode(data[self.CONST_QUERY]) auth_cookie = self.create_cookie(uid, "samlm") resp = Redirect(return_to, headers=[auth_cookie]) return resp def setup_userdb(self, uid, samldata): attributes = {} if self.sp_conf.ATTRIBUTE_WHITELIST is not None: for attr, allowed in self.sp_conf.ATTRIBUTE_WHITELIST.iteritems(): if attr in samldata: if allowed is not None: tmp_attr_list = [] for tmp_value in samldata[attr]: for allowed_str in allowed: if allowed_str in tmp_value: tmp_attr_list.append(tmp_value) if len(tmp_attr_list) > 0: attributes[attr] = tmp_attr_list
def application(environ, start_response): session = environ['beaker.session'] path = environ.get('PATH_INFO', '').lstrip('/') if path == "robots.txt": return static(environ, start_response, LOGGER, "static/robots.txt") if path.startswith("static/"): return static(environ, start_response, LOGGER, path) query = parse_qs(environ["QUERY_STRING"]) if path == "rp": # After having chosen which OP to authenticate at if "uid" in query: client = CLIENTS.dynamic_client(query["uid"][0]) session["op"] = client.provider_info["issuer"] else: client = CLIENTS[query["op"][0]] session["op"] = query["op"][0] try: resp = client.create_authn_request(session) except Exception: raise else: return resp(environ, start_response) elif path == "authz_cb": # After having authenticated at the OP client = CLIENTS[session["op"]] try: userinfo = client.callback(query) except OIDCError as err: return operror(environ, start_response, "%s" % err) except Exception: raise else: return opresult(environ, start_response, userinfo) elif path == "logout": # After the user has pressed the logout button client = CLIENTS[session["op"]] logout_url = client.endsession_endpoint try: # Specify to which URL the OP should return the user after # log out. That URL must be registered with the OP at client # registration. logout_url += "?" + urllib.urlencode({ "post_logout_redirect_uri": client.registration_response["post_logout_redirect_uris"][0] }) except KeyError: pass else: # If there is an ID token send it along as a id_token_hint _idtoken = get_id_token(client, session) if _idtoken: logout_url += "&" + urllib.urlencode({ "id_token_hint": id_token_as_signed_jwt(client, _idtoken, "HS256") }) clear_session(session) resp = Redirect(str(logout_url)) return resp(environ, start_response) return opchoice(environ, start_response, CLIENTS)
def auth_callback(): """ Auth callback: authenticate the user and get an access token """ login_hint_token = request.args.get('login_hint_token') error = request.args.get('error') state = request.args.get('state') code = request.args.get('code') current_user = get_current_user(session) redirect_uri = url_for('auth_callback', _external=True, _scheme=('http' if IS_LOCAL else 'https')) zenkey_oidc_service = ZenKeyOIDCService(CLIENT_ID, CLIENT_SECRET, redirect_uri, session_service) auth_flow_handler = AuthorizationFlowHandler(session) # handle errors returned from ZenKey if error is not None: # if an error happens, delete the auth information saved in the session auth_flow_handler.delete_authorization_details() session_service.clear() raise Exception(error) # check if the user is already logged in if current_user is not None and not auth_flow_handler.authorization_in_progress( ): return redirect('/') # use a cached MCCMNC if needed mccmnc = request.args.get('mccmnc', session_service.get_mccmnc()) # If we have no mccmnc, begin the carrier discovery process if mccmnc is None: return redirect('/auth') if state is None: # if an error happens, delete the auth information saved in the session auth_flow_handler.delete_authorization_details() raise Exception('missing state') # build our OpenID client openid_client = Client(client_authn_method=CLIENT_AUTHN_METHOD, client_id=CLIENT_ID) # save the client information to the OIDC client client_registration_info = RegistrationResponse( **{ "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET }) openid_client.store_registration_info(client_registration_info) # discover the carrier OIDC endpoint configuration oidc_configuration = zenkey_oidc_service.discover_oidc_provider_metadata( mccmnc) # save the provider config to the OIDC client after we've discovered it provider_configuration = ProviderConfigurationResponse( **oidc_configuration) openid_client.handle_provider_config(provider_configuration, provider_configuration['issuer'], True, True) if code is None: # Request an auth code # The carrier discovery endpoint has redirected back to our app with the mccmnc. # Now we can start the authorize flow by requesting an auth code. # Send the user to the ZenKey authorization endpoint. After authorization, this endpoint # will redirect back to our app with an auth code. if auth_flow_handler.authorization_in_progress(): # authorization is in progress auth_kwargs = { # only openid scope is needed for this auth request 'scope': ['openid'], # add the context and acr value to the auth request 'context': auth_flow_handler.get_authorization_details().get('context'), 'acr_values': 'a3' } else: # no authorization in progress: do a standard login authorization auth_kwargs = {'scope': SCOPE} authorization_url = zenkey_oidc_service.get_auth_code_request_url( openid_client, login_hint_token, state, mccmnc, **auth_kwargs) return Redirect(authorization_url) if code: # Token exchange: # Now that the Auth redirect has returned to our app with an auth code, we can # do the token exchange. # Exchange the auth code for a token and then call the userinfo endpoint. token_response = zenkey_oidc_service.request_token( openid_client, request.environ['QUERY_STRING']) # if auth in progress, do the auth thing # otherwise do the userinfo call and login if auth_flow_handler.authorization_in_progress(): return auth_flow_handler.success_router(token_response) # fetch the userinfo from the API userinfo = zenkey_oidc_service.get_userinfo( openid_client, token_response["access_token"]) # this is where a real app might look up the user in the database using the "sub" value # we could also create a new user or show a registration form # the userinfo object contains values like sub, name, and email (depending on which # scopes were requested) # these values can be saved for the user or used to auto-populate a registration form # save the userinfo in the session and return to the homepage: now the user is logged in session['userinfo'] = json.dumps(userinfo.to_dict()) return redirect('/') # If we have no mccmnc, begin the carrier discovery process return redirect('/auth')