mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-08 08:28:15 -04:00
Properly handling and validating the state for login.gov
Signed-off-by: Cliff Hill <clifford.hill@gsa.gov>
This commit is contained in:
@@ -35,24 +35,24 @@ def index():
|
|||||||
if current_user and current_user.is_authenticated:
|
if current_user and current_user.is_authenticated:
|
||||||
return redirect(url_for("main.choose_account"))
|
return redirect(url_for("main.choose_account"))
|
||||||
|
|
||||||
token = generate_token(
|
# make and store the state
|
||||||
|
state = generate_token(
|
||||||
str(request.remote_addr),
|
str(request.remote_addr),
|
||||||
current_app.config["SECRET_KEY"],
|
current_app.config["SECRET_KEY"],
|
||||||
current_app.config["DANGEROUS_SALT"],
|
current_app.config["DANGEROUS_SALT"],
|
||||||
)
|
)
|
||||||
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
|
state_key = f"login-nonce-{unquote(state)}"
|
||||||
# handle unit tests
|
redis_client.set(state_key, state)
|
||||||
|
|
||||||
current_app.logger.warning(f"############### {str(request.remote_addr)}")
|
|
||||||
|
|
||||||
|
# make and store the nonce
|
||||||
nonce = secrets.token_urlsafe()
|
nonce = secrets.token_urlsafe()
|
||||||
|
nonce_key = f"login-nonce-{unquote(nonce)}"
|
||||||
|
redis_client.set(nonce_key, nonce)
|
||||||
|
|
||||||
redis_key = f"login-nonce-{unquote(nonce)}"
|
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
|
||||||
redis_client.set(redis_key, nonce)
|
|
||||||
|
|
||||||
if url is not None:
|
if url is not None:
|
||||||
url = url.replace("NONCE", nonce)
|
url = url.replace("NONCE", nonce)
|
||||||
url = url.replace("STATE", token)
|
url = url.replace("STATE", state)
|
||||||
return render_template(
|
return render_template(
|
||||||
"views/signedout.html",
|
"views/signedout.html",
|
||||||
sms_rate=CURRENT_SMS_RATE,
|
sms_rate=CURRENT_SMS_RATE,
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ def _get_access_token(code): # pragma: no cover
|
|||||||
response_json = response.json()
|
response_json = response.json()
|
||||||
id_token = get_id_token(response_json)
|
id_token = get_id_token(response_json)
|
||||||
nonce = id_token["nonce"]
|
nonce = id_token["nonce"]
|
||||||
redis_key = f"login-nonce-{unquote(nonce)}"
|
nonce_key = f"login-nonce-{unquote(nonce)}"
|
||||||
stored_nonce = redis_client.get(redis_key).decode("utf8")
|
stored_nonce = redis_client.get(nonce_key).decode("utf8")
|
||||||
|
|
||||||
if nonce != stored_nonce:
|
if nonce != stored_nonce:
|
||||||
current_app.logger.error(f"Nonce Error: {nonce} != {stored_nonce}")
|
current_app.logger.error(f"Nonce Error: {nonce} != {stored_nonce}")
|
||||||
@@ -99,6 +99,12 @@ def _do_login_dot_gov(): # $ pragma: no cover
|
|||||||
# start login.gov
|
# start login.gov
|
||||||
code = request.args.get("code")
|
code = request.args.get("code")
|
||||||
state = request.args.get("state")
|
state = request.args.get("state")
|
||||||
|
state_key = f"login-state-{unquote(state)}"
|
||||||
|
stored_state = redis_client.get(state_key).decode("utf8")
|
||||||
|
if state != stored_state:
|
||||||
|
current_app.logger.error(f"State Error: {state} != {stored_state}")
|
||||||
|
abort(403)
|
||||||
|
|
||||||
login_gov_error = request.args.get("error")
|
login_gov_error = request.args.get("error")
|
||||||
|
|
||||||
if login_gov_error:
|
if login_gov_error:
|
||||||
@@ -203,21 +209,23 @@ def sign_in(): # pragma: no cover
|
|||||||
return redirect(redirect_url)
|
return redirect(redirect_url)
|
||||||
return redirect(url_for("main.show_accounts_or_dashboard"))
|
return redirect(url_for("main.show_accounts_or_dashboard"))
|
||||||
|
|
||||||
token = generate_token(
|
state = generate_token(
|
||||||
str(request.remote_addr),
|
str(request.remote_addr),
|
||||||
current_app.config["SECRET_KEY"],
|
current_app.config["SECRET_KEY"],
|
||||||
current_app.config["DANGEROUS_SALT"],
|
current_app.config["DANGEROUS_SALT"],
|
||||||
)
|
)
|
||||||
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
|
state_key = f"login-state-{unquote(state)}"
|
||||||
|
redis_client.set(state_key, state)
|
||||||
|
|
||||||
nonce = secrets.token_urlsafe()
|
nonce = secrets.token_urlsafe()
|
||||||
redis_key = f"login-nonce-{unquote(nonce)}"
|
nonce_key = f"login-nonce-{unquote(nonce)}"
|
||||||
redis_client.set(redis_key, nonce)
|
redis_client.set(nonce_key, nonce)
|
||||||
|
|
||||||
|
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
|
||||||
# handle unit tests
|
# handle unit tests
|
||||||
if url is not None:
|
if url is not None:
|
||||||
url = url.replace("NONCE", nonce)
|
url = url.replace("NONCE", nonce)
|
||||||
url = url.replace("STATE", token)
|
url = url.replace("STATE", state)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"views/signin.html",
|
"views/signin.html",
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import secrets
|
import secrets
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
|
from flask import current_app, request
|
||||||
|
|
||||||
from app import redis_client
|
from app import redis_client
|
||||||
from app.notify_client import NotifyAdminAPIClient, _attach_current_user, cache
|
from app.notify_client import NotifyAdminAPIClient, _attach_current_user, cache
|
||||||
from app.utils.user_permissions import (
|
from app.utils.user_permissions import (
|
||||||
all_ui_permissions,
|
all_ui_permissions,
|
||||||
translate_permissions_from_ui_to_db,
|
translate_permissions_from_ui_to_db,
|
||||||
)
|
)
|
||||||
|
from notifications_utils.url_safe_token import generate_token
|
||||||
|
|
||||||
|
|
||||||
class InviteApiClient(NotifyAdminAPIClient):
|
class InviteApiClient(NotifyAdminAPIClient):
|
||||||
@@ -37,11 +40,21 @@ class InviteApiClient(NotifyAdminAPIClient):
|
|||||||
}
|
}
|
||||||
data = _attach_current_user(data)
|
data = _attach_current_user(data)
|
||||||
|
|
||||||
|
# make and store the state
|
||||||
|
state = generate_token(
|
||||||
|
str(request.remote_addr),
|
||||||
|
current_app.config["SECRET_KEY"],
|
||||||
|
current_app.config["DANGEROUS_SALT"],
|
||||||
|
)
|
||||||
|
state_key = f"login-state-{unquote(state)}"
|
||||||
|
redis_client.set(state_key, state)
|
||||||
|
|
||||||
# make and store the nonce
|
# make and store the nonce
|
||||||
nonce = secrets.token_urlsafe()
|
nonce = secrets.token_urlsafe()
|
||||||
redis_key = f"login-nonce-{unquote(nonce)}"
|
redis_key = f"login-nonce-{unquote(nonce)}"
|
||||||
redis_client.set(f"{redis_key}", nonce) # save the nonce to redis.
|
redis_client.set(f"{redis_key}", nonce) # save the nonce to redis.
|
||||||
data["nonce"] = nonce # This is passed to api for the invite url.
|
data["nonce"] = nonce # This is passed to api for the invite url.
|
||||||
|
data["state"] = state # This is passed to api for the invite url.
|
||||||
|
|
||||||
resp = self.post(url=f"/service/{service_id}/invite", data=data)
|
resp = self.post(url=f"/service/{service_id}/invite", data=data)
|
||||||
return resp["data"]
|
return resp["data"]
|
||||||
|
|||||||
Reference in New Issue
Block a user