File size: 4,498 Bytes
b0c3a57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1f7c87f
 
b0c3a57
 
 
 
 
 
 
 
 
 
6e640fe
 
b0c3a57
 
 
6e640fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0c3a57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1f7c87f
b0c3a57
 
 
1f7c87f
b0c3a57
 
 
 
 
 
 
 
 
 
1f7c87f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"""OphthalmoCapture — Basic Authentication Service

Provides a simple login gate using streamlit-authenticator.
Doctors must authenticate before accessing the labeling interface.
Their name is automatically set in the session for audit trails.

If streamlit-authenticator is not installed, authentication is skipped
and the app works in "anonymous" mode.
"""

import streamlit as st

try:
    import streamlit_authenticator as stauth
    AUTH_AVAILABLE = True
except ImportError:
    AUTH_AVAILABLE = False

from i18n import t


# ── Default credentials ──────────────────────────────────────────────────────
# In production, load these from a secure YAML/env.  For now, hardcoded demo.
DEFAULT_CREDENTIALS = {
    "usernames": {
        "admin": {
            "name": "Administrador",
            "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq",
            # plain: "admin123"  — generate new hashes with stauth.Hasher
        },
        "Saul": {
            "name": "Dr. Saul",
            "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq",
            # plain: "admin123"
        },
        "Marisse": {
            "name": "Dra. Marisse",
            "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq",
            # plain: "admin123"
        },
        "Angel": {
            "name": "Dr. Angel",
            "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq",
            # plain: "admin123"
        },
        "Enmanuel": {
            "name": "Dr. Enmanuel",
            "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq",
            # plain: "admin123"
        },
        "Micaela": {
            "name": "Dra. Micaela",
            "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq",
            # plain: "admin123"
        },
        "Miguel": {
            "name": "Dr. Miguel",
            "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq",
            # plain: "admin123"
        },
    }
}

COOKIE_NAME = "ophthalmocapture_auth"
COOKIE_KEY = "ophthalmocapture_secret_key"
COOKIE_EXPIRY_DAYS = 1


def _get_authenticator():
    """Return a single shared Authenticate instance per session."""
    if "authenticator" not in st.session_state:
        st.session_state["authenticator"] = stauth.Authenticate(
            credentials=DEFAULT_CREDENTIALS,
            cookie_name=COOKIE_NAME,
            cookie_key=COOKIE_KEY,
            cookie_expiry_days=COOKIE_EXPIRY_DAYS,
        )
    return st.session_state["authenticator"]


def require_auth() -> bool:
    """Show login form and return True if the user is authenticated.

    If streamlit-authenticator is not installed, returns True immediately
    (anonymous mode) and sets doctor_name to empty string.
    """
    if not AUTH_AVAILABLE:
        # Graceful degradation: no auth library → anonymous mode
        return True

    authenticator = _get_authenticator()

    try:
        authenticator.login(location="main")
    except Exception:
        pass

    if st.session_state.get("authentication_status"):
        # Set doctor name from authenticated user
        username = st.session_state.get("username", "")
        user_info = DEFAULT_CREDENTIALS["usernames"].get(username, {})
        st.session_state.doctor_name = user_info.get("name", username)
        return True

    elif st.session_state.get("authentication_status") is False:
        st.error(t("login_error"))
        return False

    else:
        st.info(t("login_prompt"))
        return False


def render_logout_button():
    """Show a logout button in the sidebar (only if auth is active)."""
    if not AUTH_AVAILABLE:
        return

    if st.session_state.get("authentication_status"):
        authenticator = _get_authenticator()
        authenticator.logout(t("logout"), location="sidebar")


def do_logout():
    """Programmatically log out the current user."""
    if not AUTH_AVAILABLE:
        return
    try:
        authenticator = _get_authenticator()
        authenticator.logout(location="unrendered")
    except Exception:
        # Fallback: clear auth keys manually
        for key in ("authentication_status", "username", "name", "logout"):
            st.session_state.pop(key, None)
        st.session_state.pop("authenticator", None)