mrfakename commited on
Commit
6aa12cc
·
1 Parent(s): 31782e0
Files changed (5) hide show
  1. README.md +5 -0
  2. app.py +126 -0
  3. requirements.txt +2 -1
  4. templates/dashboard.html +142 -0
  5. templates/index.html +78 -0
README.md CHANGED
@@ -8,6 +8,11 @@ sdk_version: 6.0.2
8
  app_file: app.py
9
  pinned: false
10
  short_description: Manage your Hugging Face Spaces
 
 
 
 
 
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
8
  app_file: app.py
9
  pinned: false
10
  short_description: Manage your Hugging Face Spaces
11
+ hf_oauth: true
12
+ hf_oauth_expiration_minutes: 480
13
+ hf_oauth_scopes:
14
+ - read-repos
15
+ - manage-repos
16
  ---
17
 
18
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import secrets
3
+ import urllib.parse
4
+ from flask import Flask, redirect, request, session, render_template, url_for
5
+ import requests
6
+
7
+ app = Flask(__name__)
8
+ app.secret_key = os.environ.get("SECRET_KEY", secrets.token_hex(32))
9
+
10
+ # OAuth configuration from HF Space environment
11
+ OAUTH_CLIENT_ID = os.environ.get("OAUTH_CLIENT_ID")
12
+ OAUTH_CLIENT_SECRET = os.environ.get("OAUTH_CLIENT_SECRET")
13
+ OPENID_PROVIDER_URL = os.environ.get("OPENID_PROVIDER_URL", "https://huggingface.co")
14
+ SPACE_HOST = os.environ.get("SPACE_HOST", "localhost:7860")
15
+
16
+ # Determine the base URL for redirects
17
+ def get_base_url():
18
+ if "localhost" in SPACE_HOST or "127.0.0.1" in SPACE_HOST:
19
+ return f"http://{SPACE_HOST}"
20
+ return f"https://{SPACE_HOST}"
21
+
22
+ @app.route("/")
23
+ def index():
24
+ if "user" in session:
25
+ return redirect(url_for("dashboard"))
26
+ return render_template("index.html")
27
+
28
+ @app.route("/login")
29
+ def login():
30
+ if not OAUTH_CLIENT_ID:
31
+ return "OAuth not configured. Make sure hf_oauth: true is set in your Space's README.md", 500
32
+
33
+ # Generate state for CSRF protection
34
+ state = secrets.token_urlsafe(32)
35
+ session["oauth_state"] = state
36
+
37
+ # Build authorization URL
38
+ redirect_uri = f"{get_base_url()}/login/callback"
39
+ params = {
40
+ "client_id": OAUTH_CLIENT_ID,
41
+ "redirect_uri": redirect_uri,
42
+ "scope": "openid profile",
43
+ "response_type": "code",
44
+ "state": state,
45
+ }
46
+ auth_url = f"{OPENID_PROVIDER_URL}/oauth/authorize?{urllib.parse.urlencode(params)}"
47
+
48
+ return redirect(auth_url)
49
+
50
+ @app.route("/login/callback")
51
+ def callback():
52
+ # Verify state
53
+ state = request.args.get("state")
54
+ if state != session.get("oauth_state"):
55
+ return "Invalid state parameter", 400
56
+
57
+ # Get authorization code
58
+ code = request.args.get("code")
59
+ if not code:
60
+ error = request.args.get("error", "Unknown error")
61
+ return f"Authorization failed: {error}", 400
62
+
63
+ # Exchange code for tokens
64
+ redirect_uri = f"{get_base_url()}/login/callback"
65
+ token_url = f"{OPENID_PROVIDER_URL}/oauth/token"
66
+
67
+ token_response = requests.post(
68
+ token_url,
69
+ data={
70
+ "client_id": OAUTH_CLIENT_ID,
71
+ "client_secret": OAUTH_CLIENT_SECRET,
72
+ "code": code,
73
+ "grant_type": "authorization_code",
74
+ "redirect_uri": redirect_uri,
75
+ },
76
+ headers={
77
+ "Content-Type": "application/x-www-form-urlencoded",
78
+ },
79
+ )
80
+
81
+ if token_response.status_code != 200:
82
+ return f"Token exchange failed: {token_response.text}", 400
83
+
84
+ tokens = token_response.json()
85
+ access_token = tokens.get("access_token")
86
+
87
+ # Get user info
88
+ userinfo_url = f"{OPENID_PROVIDER_URL}/oauth/userinfo"
89
+ userinfo_response = requests.get(
90
+ userinfo_url,
91
+ headers={"Authorization": f"Bearer {access_token}"},
92
+ )
93
+
94
+ if userinfo_response.status_code != 200:
95
+ return f"Failed to get user info: {userinfo_response.text}", 400
96
+
97
+ user_info = userinfo_response.json()
98
+
99
+ # Store user info in session
100
+ session["user"] = {
101
+ "sub": user_info.get("sub"),
102
+ "username": user_info.get("preferred_username"),
103
+ "name": user_info.get("name"),
104
+ "email": user_info.get("email"),
105
+ "avatar_url": user_info.get("picture"),
106
+ }
107
+ session["access_token"] = access_token
108
+
109
+ # Clean up state
110
+ session.pop("oauth_state", None)
111
+
112
+ return redirect(url_for("dashboard"))
113
+
114
+ @app.route("/dashboard")
115
+ def dashboard():
116
+ if "user" not in session:
117
+ return redirect(url_for("index"))
118
+ return render_template("dashboard.html", user=session["user"])
119
+
120
+ @app.route("/logout")
121
+ def logout():
122
+ session.clear()
123
+ return redirect(url_for("index"))
124
+
125
+ if __name__ == "__main__":
126
+ app.run(host="0.0.0.0", port=7860, debug=True)
requirements.txt CHANGED
@@ -1 +1,2 @@
1
- flask
 
 
1
+ flask
2
+ requests
templates/dashboard.html ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Dashboard - Spaces Dashboard</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+ body {
14
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
15
+ background: #f5f5f5;
16
+ min-height: 100vh;
17
+ }
18
+ .navbar {
19
+ background: white;
20
+ padding: 1rem 2rem;
21
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
22
+ display: flex;
23
+ justify-content: space-between;
24
+ align-items: center;
25
+ }
26
+ .navbar h1 {
27
+ font-size: 1.4rem;
28
+ color: #333;
29
+ }
30
+ .user-info {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: 1rem;
34
+ }
35
+ .avatar {
36
+ width: 40px;
37
+ height: 40px;
38
+ border-radius: 50%;
39
+ object-fit: cover;
40
+ }
41
+ .username {
42
+ font-weight: 600;
43
+ color: #333;
44
+ }
45
+ .logout-btn {
46
+ background: #ff4444;
47
+ color: white;
48
+ padding: 8px 16px;
49
+ border-radius: 6px;
50
+ text-decoration: none;
51
+ font-size: 0.9rem;
52
+ transition: background 0.2s;
53
+ }
54
+ .logout-btn:hover {
55
+ background: #cc0000;
56
+ }
57
+ .main {
58
+ max-width: 1200px;
59
+ margin: 2rem auto;
60
+ padding: 0 2rem;
61
+ }
62
+ .welcome {
63
+ background: white;
64
+ padding: 2rem;
65
+ border-radius: 12px;
66
+ margin-bottom: 2rem;
67
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
68
+ }
69
+ .welcome h2 {
70
+ color: #333;
71
+ margin-bottom: 0.5rem;
72
+ }
73
+ .welcome p {
74
+ color: #666;
75
+ }
76
+ .info-grid {
77
+ display: grid;
78
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
79
+ gap: 1rem;
80
+ }
81
+ .info-card {
82
+ background: white;
83
+ padding: 1.5rem;
84
+ border-radius: 12px;
85
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
86
+ }
87
+ .info-card h3 {
88
+ color: #333;
89
+ margin-bottom: 0.5rem;
90
+ font-size: 0.9rem;
91
+ text-transform: uppercase;
92
+ letter-spacing: 0.5px;
93
+ }
94
+ .info-card p {
95
+ color: #666;
96
+ font-size: 1.1rem;
97
+ }
98
+ </style>
99
+ </head>
100
+ <body>
101
+ <nav class="navbar">
102
+ <h1>Spaces Dashboard</h1>
103
+ <div class="user-info">
104
+ {% if user.avatar_url %}
105
+ <img src="{{ user.avatar_url }}" alt="Avatar" class="avatar">
106
+ {% endif %}
107
+ <span class="username">{{ user.username }}</span>
108
+ <a href="/logout" class="logout-btn">Logout</a>
109
+ </div>
110
+ </nav>
111
+
112
+ <main class="main">
113
+ <div class="welcome">
114
+ <h2>Welcome, {{ user.name or user.username }}!</h2>
115
+ <p>You have successfully signed in with your Hugging Face account.</p>
116
+ </div>
117
+
118
+ <div class="info-grid">
119
+ <div class="info-card">
120
+ <h3>Username</h3>
121
+ <p>{{ user.username }}</p>
122
+ </div>
123
+ {% if user.name %}
124
+ <div class="info-card">
125
+ <h3>Name</h3>
126
+ <p>{{ user.name }}</p>
127
+ </div>
128
+ {% endif %}
129
+ {% if user.email %}
130
+ <div class="info-card">
131
+ <h3>Email</h3>
132
+ <p>{{ user.email }}</p>
133
+ </div>
134
+ {% endif %}
135
+ <div class="info-card">
136
+ <h3>User ID</h3>
137
+ <p>{{ user.sub }}</p>
138
+ </div>
139
+ </div>
140
+ </main>
141
+ </body>
142
+ </html>
templates/index.html ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Spaces Dashboard</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+ body {
14
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
15
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
16
+ min-height: 100vh;
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ }
21
+ .container {
22
+ background: white;
23
+ padding: 3rem;
24
+ border-radius: 16px;
25
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
26
+ text-align: center;
27
+ max-width: 400px;
28
+ width: 90%;
29
+ }
30
+ h1 {
31
+ color: #333;
32
+ margin-bottom: 0.5rem;
33
+ font-size: 1.8rem;
34
+ }
35
+ .subtitle {
36
+ color: #666;
37
+ margin-bottom: 2rem;
38
+ }
39
+ .login-btn {
40
+ display: inline-flex;
41
+ align-items: center;
42
+ gap: 10px;
43
+ background: #ffcc00;
44
+ color: #1a1a1a;
45
+ padding: 14px 28px;
46
+ border-radius: 8px;
47
+ text-decoration: none;
48
+ font-weight: 600;
49
+ font-size: 1rem;
50
+ transition: all 0.2s ease;
51
+ border: none;
52
+ cursor: pointer;
53
+ }
54
+ .login-btn:hover {
55
+ background: #e6b800;
56
+ transform: translateY(-2px);
57
+ box-shadow: 0 4px 12px rgba(255, 204, 0, 0.4);
58
+ }
59
+ .login-btn svg {
60
+ width: 24px;
61
+ height: 24px;
62
+ }
63
+ </style>
64
+ </head>
65
+ <body>
66
+ <div class="container">
67
+ <h1>Spaces Dashboard</h1>
68
+ <p class="subtitle">Manage your Hugging Face Spaces</p>
69
+ <a href="/login" class="login-btn">
70
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
71
+ <path fill="#FFD21E" d="M50 5C25.1 5 5 25.1 5 50s20.1 45 45 45 45-20.1 45-45S74.9 5 50 5z"/>
72
+ <path fill="#1a1a1a" d="M35 40c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm30 0c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8z"/>
73
+ </svg>
74
+ Sign in with Hugging Face
75
+ </a>
76
+ </div>
77
+ </body>
78
+ </html>