Spaces:
Running
Running
Rename app.py to Files.Backup.AuthWorksapp.py
Browse files- Files.Backup.AuthWorksapp.py +162 -0
- app.py +0 -209
Files.Backup.AuthWorksapp.py
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import streamlit as st
|
3 |
+
import requests
|
4 |
+
import msal
|
5 |
+
import urllib.parse
|
6 |
+
import base64
|
7 |
+
import hashlib
|
8 |
+
import secrets
|
9 |
+
|
10 |
+
# 🤓 Load environment variables (Ensure these are set!)
|
11 |
+
APPLICATION_ID_KEY = os.getenv('APPLICATION_ID_KEY')
|
12 |
+
CLIENT_SECRET_KEY = os.getenv('CLIENT_SECRET_KEY')
|
13 |
+
AUTHORITY_URL = 'https://login.microsoftonline.com/common'
|
14 |
+
REDIRECT_URI = 'https://huggingface.co/spaces/awacke1/MSGraphAPI'
|
15 |
+
|
16 |
+
# Define product to scope mapping
|
17 |
+
PRODUCT_SCOPES = {
|
18 |
+
"📧 Outlook": ['Mail.Read', 'Mail.Send', 'Calendars.ReadWrite'],
|
19 |
+
"📒 OneNote": ['Notes.Read', 'Notes.Create'],
|
20 |
+
"📊 Excel": ['Files.ReadWrite.All'],
|
21 |
+
"📄 Word": ['Files.ReadWrite.All'],
|
22 |
+
"🗃️ SharePoint": ['Sites.Read.All', 'Sites.ReadWrite.All'],
|
23 |
+
"📅 Teams": ['Team.ReadBasic.All', 'Channel.ReadBasic.All'],
|
24 |
+
"💬 Viva": ['Analytics.Read'],
|
25 |
+
"🚀 Power Platform": ['Flow.Read.All'],
|
26 |
+
"🧠 Copilot": ['Cognitive.Read'],
|
27 |
+
"🗂️ OneDrive": ['Files.ReadWrite.All'],
|
28 |
+
"💡 PowerPoint": ['Files.ReadWrite.All'],
|
29 |
+
"📚 Microsoft Bookings": ['Bookings.Read.All', 'Bookings.ReadWrite.All'],
|
30 |
+
"📓 Loop": ['Files.ReadWrite.All'],
|
31 |
+
"🗣️ Translator": ['Translation.Read'],
|
32 |
+
"📋 To Do & Planner": ['Tasks.ReadWrite'],
|
33 |
+
"🔗 Azure OpenAI Service": ['AzureAIServices.ReadWrite.All']
|
34 |
+
}
|
35 |
+
|
36 |
+
# Always include these base scopes
|
37 |
+
BASE_SCOPES = ['User.Read', 'openid', 'profile', 'offline_access']
|
38 |
+
|
39 |
+
# Function to generate PKCE code verifier and challenge
|
40 |
+
def generate_pkce_codes():
|
41 |
+
code_verifier = secrets.token_urlsafe(128)[:128]
|
42 |
+
code_challenge = base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest()).decode().rstrip('=')
|
43 |
+
return code_verifier, code_challenge
|
44 |
+
|
45 |
+
def get_msal_app(code_challenge=None):
|
46 |
+
return msal.PublicClientApplication(
|
47 |
+
client_id=APPLICATION_ID_KEY,
|
48 |
+
authority=AUTHORITY_URL
|
49 |
+
)
|
50 |
+
|
51 |
+
# Function to get access token
|
52 |
+
def get_access_token(code, code_verifier):
|
53 |
+
client_instance = get_msal_app()
|
54 |
+
|
55 |
+
try:
|
56 |
+
result = client_instance.acquire_token_by_authorization_code(
|
57 |
+
code=code,
|
58 |
+
scopes=st.session_state.get('scopes', BASE_SCOPES),
|
59 |
+
redirect_uri=REDIRECT_URI,
|
60 |
+
code_verifier=code_verifier
|
61 |
+
)
|
62 |
+
|
63 |
+
if 'access_token' in result:
|
64 |
+
return result['access_token']
|
65 |
+
else:
|
66 |
+
error_description = result.get('error_description', 'No error description provided')
|
67 |
+
raise Exception(f"Error acquiring token: {error_description}")
|
68 |
+
except Exception as e:
|
69 |
+
st.error(f"Exception in get_access_token: {str(e)}")
|
70 |
+
raise
|
71 |
+
|
72 |
+
# Main application function
|
73 |
+
def main():
|
74 |
+
st.title("🦄 MS Graph API with AI & Cloud Integration for M365")
|
75 |
+
|
76 |
+
# Sidebar for product selection
|
77 |
+
st.sidebar.title("📝 M365 Products")
|
78 |
+
st.sidebar.write("Select products to integrate:")
|
79 |
+
|
80 |
+
selected_products = {}
|
81 |
+
for product in PRODUCT_SCOPES.keys():
|
82 |
+
selected = st.sidebar.checkbox(product)
|
83 |
+
if selected:
|
84 |
+
selected_products[product] = True
|
85 |
+
|
86 |
+
# Dynamically build scopes based on selected products
|
87 |
+
scopes = BASE_SCOPES.copy()
|
88 |
+
for product in selected_products:
|
89 |
+
scopes.extend(PRODUCT_SCOPES[product])
|
90 |
+
scopes = list(set(scopes)) # Remove duplicates
|
91 |
+
st.session_state['scopes'] = scopes
|
92 |
+
|
93 |
+
# Authentication flow
|
94 |
+
if 'access_token' not in st.session_state:
|
95 |
+
if 'code_verifier' not in st.session_state:
|
96 |
+
code_verifier, code_challenge = generate_pkce_codes()
|
97 |
+
st.session_state['code_verifier'] = code_verifier
|
98 |
+
else:
|
99 |
+
code_verifier = st.session_state['code_verifier']
|
100 |
+
code_challenge = base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest()).decode().rstrip('=')
|
101 |
+
|
102 |
+
client_instance = get_msal_app()
|
103 |
+
auth_url = client_instance.get_authorization_request_url(
|
104 |
+
scopes=scopes,
|
105 |
+
redirect_uri=REDIRECT_URI,
|
106 |
+
code_challenge=code_challenge,
|
107 |
+
code_challenge_method="S256"
|
108 |
+
)
|
109 |
+
st.write('👋 Please [click here]({}) to log in and authorize the app.'.format(auth_url))
|
110 |
+
|
111 |
+
# Check for authorization code in query parameters
|
112 |
+
query_params = st.query_params
|
113 |
+
if 'code' in query_params:
|
114 |
+
code = query_params.get('code')
|
115 |
+
st.write('🔑 Authorization Code Obtained:', code[:10] + '...')
|
116 |
+
|
117 |
+
try:
|
118 |
+
access_token = get_access_token(code, code_verifier)
|
119 |
+
st.session_state['access_token'] = access_token
|
120 |
+
st.success("Access token acquired successfully!")
|
121 |
+
st.rerun()
|
122 |
+
except Exception as e:
|
123 |
+
st.error(f"Error acquiring access token: {str(e)}")
|
124 |
+
st.stop()
|
125 |
+
else:
|
126 |
+
# User is authenticated, proceed with the app
|
127 |
+
access_token = st.session_state['access_token']
|
128 |
+
|
129 |
+
# Greet the user
|
130 |
+
user_info = get_user_info(access_token)
|
131 |
+
if user_info:
|
132 |
+
st.sidebar.write(f"👋 Hello, {user_info.get('displayName', 'User')}!")
|
133 |
+
|
134 |
+
# Handle selected products
|
135 |
+
if selected_products:
|
136 |
+
st.header("🧩 M365 Product Integrations")
|
137 |
+
for product in selected_products:
|
138 |
+
st.subheader(f"{product}")
|
139 |
+
handle_product_integration(access_token, product)
|
140 |
+
else:
|
141 |
+
st.write("No products selected. Please select products from the sidebar.")
|
142 |
+
|
143 |
+
# Function to get user info
|
144 |
+
def get_user_info(access_token):
|
145 |
+
headers = {'Authorization': f'Bearer {access_token}'}
|
146 |
+
response = requests.get('https://graph.microsoft.com/v1.0/me', headers=headers)
|
147 |
+
if response.status_code == 200:
|
148 |
+
return response.json()
|
149 |
+
else:
|
150 |
+
st.error('Failed to fetch user info.')
|
151 |
+
return None
|
152 |
+
|
153 |
+
# Function to handle product integration
|
154 |
+
def handle_product_integration(access_token, product):
|
155 |
+
# Implement the integration logic for each product here
|
156 |
+
# This is a placeholder - you'll need to implement the actual API calls
|
157 |
+
st.write(f"Integrating {product}...")
|
158 |
+
# Example: if product == "📧 Outlook": get_outlook_data(access_token)
|
159 |
+
|
160 |
+
# Run the main function
|
161 |
+
if __name__ == "__main__":
|
162 |
+
main()
|
app.py
DELETED
@@ -1,209 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import streamlit as st
|
3 |
-
import requests
|
4 |
-
import msal
|
5 |
-
import secrets
|
6 |
-
from urllib.parse import urlencode
|
7 |
-
from datetime import datetime, timedelta
|
8 |
-
|
9 |
-
# Configuration
|
10 |
-
APPLICATION_ID = os.getenv('APPLICATION_ID_KEY')
|
11 |
-
CLIENT_SECRET = os.getenv('CLIENT_SECRET_KEY')
|
12 |
-
AUTHORITY = 'https://login.microsoftonline.com/common'
|
13 |
-
REDIRECT_URI = 'https://huggingface.co/spaces/awacke1/MSGraphAPI'
|
14 |
-
SCOPES = ['User.Read', 'Calendars.ReadWrite', 'Mail.ReadWrite', 'Files.ReadWrite.All']
|
15 |
-
|
16 |
-
# MSAL setup
|
17 |
-
def get_msal_app():
|
18 |
-
return msal.ConfidentialClientApplication(
|
19 |
-
client_id=APPLICATION_ID,
|
20 |
-
client_credential=CLIENT_SECRET,
|
21 |
-
authority=AUTHORITY
|
22 |
-
)
|
23 |
-
|
24 |
-
# Authentication functions
|
25 |
-
def generate_auth_url():
|
26 |
-
msal_app = get_msal_app()
|
27 |
-
state = secrets.token_urlsafe(32)
|
28 |
-
auth_url = msal_app.get_authorization_request_url(
|
29 |
-
scopes=SCOPES,
|
30 |
-
redirect_uri=REDIRECT_URI,
|
31 |
-
state=state
|
32 |
-
)
|
33 |
-
new_query_params = st.query_params.to_dict()
|
34 |
-
new_query_params['auth_state'] = state
|
35 |
-
return f"{auth_url}&{urlencode(new_query_params)}"
|
36 |
-
|
37 |
-
def get_token_from_code(code):
|
38 |
-
msal_app = get_msal_app()
|
39 |
-
result = msal_app.acquire_token_by_authorization_code(
|
40 |
-
code=code,
|
41 |
-
scopes=SCOPES,
|
42 |
-
redirect_uri=REDIRECT_URI
|
43 |
-
)
|
44 |
-
if 'access_token' in result:
|
45 |
-
return result
|
46 |
-
else:
|
47 |
-
raise Exception(f"Error acquiring token: {result.get('error_description')}")
|
48 |
-
|
49 |
-
# API call function
|
50 |
-
def make_api_call(endpoint, token, method='GET', data=None):
|
51 |
-
headers = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}
|
52 |
-
url = f'https://graph.microsoft.com/v1.0/{endpoint}'
|
53 |
-
|
54 |
-
if method == 'GET':
|
55 |
-
response = requests.get(url, headers=headers)
|
56 |
-
elif method == 'POST':
|
57 |
-
response = requests.post(url, headers=headers, json=data)
|
58 |
-
else:
|
59 |
-
raise ValueError(f"Unsupported method: {method}")
|
60 |
-
|
61 |
-
if response.status_code in [200, 201]:
|
62 |
-
return response.json()
|
63 |
-
else:
|
64 |
-
st.error(f"API call failed: {response.status_code} - {response.text}")
|
65 |
-
return None
|
66 |
-
|
67 |
-
# Product integration functions
|
68 |
-
def handle_outlook_integration(token):
|
69 |
-
st.subheader("📧 Outlook Integration")
|
70 |
-
emails = make_api_call('me/messages?$top=5', token)
|
71 |
-
if emails:
|
72 |
-
for email in emails['value']:
|
73 |
-
st.write(f"Subject: {email['subject']}")
|
74 |
-
st.write(f"From: {email['from']['emailAddress']['name']}")
|
75 |
-
st.write("---")
|
76 |
-
|
77 |
-
def handle_onenote_integration(token):
|
78 |
-
st.subheader("📒 OneNote Integration")
|
79 |
-
notebooks = make_api_call('me/onenote/notebooks', token)
|
80 |
-
if notebooks:
|
81 |
-
for notebook in notebooks['value']:
|
82 |
-
st.write(f"Notebook: {notebook['displayName']}")
|
83 |
-
|
84 |
-
def handle_calendar_integration(token):
|
85 |
-
st.subheader("📅 Calendar Integration")
|
86 |
-
events = make_api_call('me/events?$top=5', token)
|
87 |
-
if events:
|
88 |
-
for event in events['value']:
|
89 |
-
st.write(f"Event: {event['subject']}")
|
90 |
-
st.write(f"Start: {event['start']['dateTime']}")
|
91 |
-
st.write("---")
|
92 |
-
|
93 |
-
def handle_onedrive_integration(token):
|
94 |
-
st.subheader("🗂️ OneDrive Integration")
|
95 |
-
files = make_api_call('me/drive/root/children', token)
|
96 |
-
if files:
|
97 |
-
for file in files['value']:
|
98 |
-
st.write(f"File: {file['name']}")
|
99 |
-
st.write(f"Type: {file['file']['mimeType'] if 'file' in file else 'Folder'}")
|
100 |
-
st.write("---")
|
101 |
-
|
102 |
-
# Main application
|
103 |
-
def main():
|
104 |
-
st.title("🦄 MS Graph API with AI & Cloud Integration for M365")
|
105 |
-
|
106 |
-
# Debug information
|
107 |
-
st.sidebar.write("Debug Info:")
|
108 |
-
st.sidebar.write(f"Query Params: {st.query_params.to_dict()}")
|
109 |
-
|
110 |
-
if 'code' in st.query_params and 'state' in st.query_params:
|
111 |
-
received_state = st.query_params['state']
|
112 |
-
expected_state = st.query_params.get('auth_state')
|
113 |
-
|
114 |
-
if received_state != expected_state:
|
115 |
-
st.error(f"Invalid state parameter. Expected {expected_state}, got {received_state}")
|
116 |
-
st.error("Please try logging in again.")
|
117 |
-
st.query_params.clear()
|
118 |
-
st.rerun()
|
119 |
-
|
120 |
-
try:
|
121 |
-
token = get_token_from_code(st.query_params['code'])
|
122 |
-
st.session_state['token'] = token
|
123 |
-
st.query_params.clear()
|
124 |
-
st.success("Successfully authenticated!")
|
125 |
-
st.rerun()
|
126 |
-
except Exception as e:
|
127 |
-
st.error(f"Authentication failed: {str(e)}")
|
128 |
-
st.query_params.clear()
|
129 |
-
st.rerun()
|
130 |
-
|
131 |
-
if 'token' not in st.session_state:
|
132 |
-
auth_url = generate_auth_url()
|
133 |
-
st.write("Please log in to continue:")
|
134 |
-
st.markdown(f"[Login with Microsoft]({auth_url})")
|
135 |
-
return
|
136 |
-
|
137 |
-
# User is authenticated, show the main app
|
138 |
-
token = st.session_state['token']['access_token']
|
139 |
-
st.sidebar.success("Authenticated successfully!")
|
140 |
-
|
141 |
-
# Display user info
|
142 |
-
user_info = make_api_call('me', token)
|
143 |
-
if user_info:
|
144 |
-
st.sidebar.write(f"Welcome, {user_info.get('displayName', 'User')}!")
|
145 |
-
|
146 |
-
# App navigation
|
147 |
-
st.sidebar.title("Navigation")
|
148 |
-
app_mode = st.sidebar.selectbox("Choose the app mode",
|
149 |
-
["Dashboard", "Product Integration", "Event Management"])
|
150 |
-
|
151 |
-
if app_mode == "Dashboard":
|
152 |
-
st.header("📊 Dashboard")
|
153 |
-
# Add dashboard widgets here
|
154 |
-
|
155 |
-
elif app_mode == "Product Integration":
|
156 |
-
st.header("🧩 Product Integration")
|
157 |
-
products = {
|
158 |
-
"📧 Outlook": handle_outlook_integration,
|
159 |
-
"📒 OneNote": handle_onenote_integration,
|
160 |
-
"📅 Calendar": handle_calendar_integration,
|
161 |
-
"🗂️ OneDrive": handle_onedrive_integration
|
162 |
-
}
|
163 |
-
|
164 |
-
for product, handler in products.items():
|
165 |
-
if st.checkbox(f"Enable {product}"):
|
166 |
-
handler(token)
|
167 |
-
|
168 |
-
elif app_mode == "Event Management":
|
169 |
-
st.header("📅 Event Management")
|
170 |
-
event_action = st.radio("Choose an action", ["View Upcoming Events", "Add New Event"])
|
171 |
-
|
172 |
-
if event_action == "View Upcoming Events":
|
173 |
-
events = make_api_call('me/events?$top=10&$orderby=start/dateTime', token)
|
174 |
-
if events:
|
175 |
-
for event in events['value']:
|
176 |
-
st.write(f"Event: {event['subject']}")
|
177 |
-
st.write(f"Start: {event['start']['dateTime']}")
|
178 |
-
st.write("---")
|
179 |
-
|
180 |
-
elif event_action == "Add New Event":
|
181 |
-
subject = st.text_input("Event Subject")
|
182 |
-
start_date = st.date_input("Start Date")
|
183 |
-
start_time = st.time_input("Start Time")
|
184 |
-
duration = st.number_input("Duration (hours)", min_value=0.5, max_value=8.0, step=0.5)
|
185 |
-
|
186 |
-
if st.button("Add Event"):
|
187 |
-
start_datetime = datetime.combine(start_date, start_time)
|
188 |
-
end_datetime = start_datetime + timedelta(hours=duration)
|
189 |
-
|
190 |
-
event_data = {
|
191 |
-
"subject": subject,
|
192 |
-
"start": {
|
193 |
-
"dateTime": start_datetime.isoformat(),
|
194 |
-
"timeZone": "UTC"
|
195 |
-
},
|
196 |
-
"end": {
|
197 |
-
"dateTime": end_datetime.isoformat(),
|
198 |
-
"timeZone": "UTC"
|
199 |
-
}
|
200 |
-
}
|
201 |
-
|
202 |
-
result = make_api_call('me/events', token, method='POST', data=event_data)
|
203 |
-
if result:
|
204 |
-
st.success("Event added successfully!")
|
205 |
-
else:
|
206 |
-
st.error("Failed to add event.")
|
207 |
-
|
208 |
-
if __name__ == "__main__":
|
209 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|