def _do_verify_2fa(user: User, secret): data = current_request.get_json() totp_value = data["totp"] totp = pyotp.TOTP(secret) if totp.verify(totp_value, valid_window=1): if not user.second_factor_auth: user.second_factor_auth = secret user.last_login_date = datetime.datetime.now() user = db.session.merge(user) db.session.commit() store_user_in_session(user, True, user.has_agreed_with_aup()) return True else: return False
def resume_session(): logger = ctx_logger("oidc") cfg = current_app.app_config oidc_config = cfg.oidc code = query_param("code", required=False, default=None) if not code: # This means that we are not in the redirect callback, but at the redirect from eduTeams logger.debug("Redirect to login in resume-session to start OIDC flow") authorization_endpoint = _get_authorization_url() return redirect(authorization_endpoint) scopes = " ".join(oidc_config.scopes) payload = { "code": code, "grant_type": "authorization_code", "scope": scopes, "redirect_uri": oidc_config.redirect_uri } headers = { "Content-Type": "application/x-www-form-urlencoded", "Cache-Control": "no-cache", "Accept": "application/json, application/json;charset=UTF-8" } response = requests.post(oidc_config.token_endpoint, data=urllib.parse.urlencode(payload), headers=headers, auth=(oidc_config.client_id, oidc_config.client_secret)) if response.status_code != 200: return _redirect_with_error(logger, f"Server error: Token endpoint error (http {response.status_code}") token_json = response.json() access_token = token_json["access_token"] headers = { "Accept": "application/json, application/json;charset=UTF-8", "Authorization": f"Bearer {access_token}" } response = requests.get(oidc_config.userinfo_endpoint, headers=headers) if response.status_code != 200: return _redirect_with_error(logger, f"Server error: User info endpoint error (http {response.status_code}") logger = ctx_logger("user") user_info_json = response.json() logger.debug(f"Userinfo endpoint results {user_info_json}") uid = user_info_json["sub"] user = User.query.filter(User.uid == uid).first() if not user: user = User(uid=uid, created_by="system", updated_by="system") add_user_claims(user_info_json, uid, user) # last_login_date is set later in this method user.last_accessed_date = datetime.datetime.now() logger.info(f"Provisioning new user {user.uid}") else: logger.info(f"Updating user {user.uid} with new claims / updated at") add_user_claims(user_info_json, uid, user) encoded_id_token = token_json["id_token"] id_token = decode_jwt_token(encoded_id_token) no_mfa_required = not oidc_config.second_factor_authentication_required idp_mfa = id_token.get("acr") == ACR_VALUES idp_allowed = mfa_idp_allowed(user, user.schac_home_organisation, None) second_factor_confirmed = no_mfa_required or idp_mfa or idp_allowed if second_factor_confirmed: user.last_login_date = datetime.datetime.now() user = db.session.merge(user) db.session.commit() user_accepted_aup = user.has_agreed_with_aup() store_user_in_session(user, second_factor_confirmed, user_accepted_aup) if not user_accepted_aup: location = f"{cfg.base_url}/aup" elif not second_factor_confirmed: location = f"{cfg.base_url}/2fa" else: location = session.get("original_destination", cfg.base_url) return redirect(location)