aven 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/app/components/aven/views/oauth/error/component.html.erb +44 -0
  4. data/app/components/aven/views/oauth/error/component.rb +30 -0
  5. data/app/components/aven/views/static/index/component.html.erb +4 -4
  6. data/app/components/aven/views/static/index/component.rb +11 -0
  7. data/app/controllers/aven/admin/base.rb +4 -4
  8. data/app/controllers/aven/application_controller.rb +22 -0
  9. data/app/controllers/aven/auth_controller.rb +6 -58
  10. data/app/controllers/aven/oauth/auth0_controller.rb +84 -0
  11. data/app/controllers/aven/oauth/base_controller.rb +183 -0
  12. data/app/controllers/aven/oauth/documentation/auth0.md +387 -0
  13. data/app/controllers/aven/oauth/documentation/entra_id.md +608 -0
  14. data/app/controllers/aven/oauth/documentation/github.md +329 -0
  15. data/app/controllers/aven/oauth/documentation/google.md +253 -0
  16. data/app/controllers/aven/oauth/entra_id_controller.rb +92 -0
  17. data/app/controllers/aven/oauth/github_controller.rb +91 -0
  18. data/app/controllers/aven/oauth/google_controller.rb +64 -0
  19. data/app/controllers/aven/workspaces_controller.rb +20 -0
  20. data/app/controllers/concerns/aven/authentication.rb +49 -0
  21. data/app/controllers/concerns/aven/controller_helpers.rb +38 -0
  22. data/app/helpers/aven/application_helper.rb +2 -6
  23. data/app/models/aven/app_record.rb +1 -1
  24. data/app/models/aven/app_record_schema.rb +0 -1
  25. data/app/models/aven/log.rb +0 -1
  26. data/app/models/aven/loggable.rb +2 -3
  27. data/app/models/aven/user.rb +0 -23
  28. data/app/models/aven/workspace.rb +49 -5
  29. data/app/models/aven/workspace_role.rb +0 -1
  30. data/app/models/aven/workspace_user.rb +0 -1
  31. data/app/models/aven/workspace_user_role.rb +0 -1
  32. data/config/routes.rb +22 -7
  33. data/db/migrate/{20251003090752_create_aven_users.rb → 20200101000001_create_aven_users.rb} +1 -1
  34. data/db/migrate/{20251004182010_create_aven_workspace_users.rb → 20200101000003_create_aven_workspace_users.rb} +1 -1
  35. data/db/migrate/{20251004182020_create_aven_workspace_roles.rb → 20200101000004_create_aven_workspace_roles.rb} +1 -1
  36. data/db/migrate/{20251004182030_create_aven_workspace_user_roles.rb → 20200101000005_create_aven_workspace_user_roles.rb} +1 -1
  37. data/db/migrate/{20251004190000_create_aven_logs.rb → 20200101000006_create_aven_logs.rb} +2 -3
  38. data/db/migrate/{20251004190100_create_aven_app_record_schemas.rb → 20200101000007_create_aven_app_record_schemas.rb} +0 -1
  39. data/db/migrate/{20251004190110_create_aven_app_records.rb → 20200101000008_create_aven_app_records.rb} +0 -1
  40. data/lib/aven/configuration.rb +26 -10
  41. data/lib/aven/engine.rb +15 -16
  42. data/lib/aven/model/tenant_model.rb +91 -0
  43. data/lib/aven/model.rb +6 -0
  44. data/lib/aven/version.rb +1 -1
  45. metadata +42 -69
  46. data/config/initializers/devise.rb +0 -43
  47. /data/db/migrate/{20251004182000_create_aven_workspaces.rb → 20200101000002_create_aven_workspaces.rb} +0 -0
  48. /data/lib/tasks/{sqema_tasks.rake → aven_tasks.rake} +0 -0
@@ -0,0 +1,608 @@
1
+ # Microsoft Entra ID OAuth Implementation Documentation
2
+
3
+ ## Overview
4
+
5
+ Microsoft Entra ID (formerly Azure Active Directory) OAuth 2.0 integration for the Aven application. This implementation supports authentication and authorization for Microsoft work/school accounts and personal Microsoft accounts.
6
+
7
+ **Implementation Date**: October 15, 2025
8
+ **Controller**: `app/controllers/aven/oauth/entra_id_controller.rb`
9
+ **Routes**: `config/routes.rb:30-32`
10
+
11
+ ---
12
+
13
+ ## Default Scopes
14
+
15
+ The implementation uses the following default scopes:
16
+
17
+ ```
18
+ openid email profile User.Read Contacts.Read Mail.Send Mail.Read
19
+ ```
20
+
21
+ ### Scope Breakdown
22
+
23
+ | Scope | Purpose | Admin Consent Required | Verified Source |
24
+ | --------------- | ------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------- |
25
+ | `openid` | OpenID Connect authentication | No | [OAuth 2.0 Scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc) |
26
+ | `email` | Access user's email address | No | [OAuth 2.0 Scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc) |
27
+ | `profile` | Access user's basic profile | No | [OAuth 2.0 Scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc) |
28
+ | `User.Read` | Read user's profile information | No | [Microsoft Graph Permissions](https://learn.microsoft.com/en-us/graph/permissions-reference) |
29
+ | `Contacts.Read` | Read user's contacts | No | [List Contacts API](https://learn.microsoft.com/en-us/graph/api/user-list-contacts) |
30
+ | `Mail.Send` | Send email as the user | No\* | [Send Mail API](https://learn.microsoft.com/en-us/graph/api/user-sendmail) |
31
+ | `Mail.Read` | Read user's email | No\* | [List Messages API](https://learn.microsoft.com/en-us/graph/api/user-list-messages) |
32
+
33
+ **Note**: "Admin Consent Required: No" means users can consent individually. However, organization admins may configure policies requiring admin pre-approval.
34
+
35
+ ---
36
+
37
+ ## Admin Consent Explanation
38
+
39
+ ### What is Admin Consent?
40
+
41
+ **User Consent (Default)**:
42
+
43
+ - Individual users can grant permissions when they sign in
44
+ - No administrator involvement needed
45
+ - Works for personal Microsoft accounts and smaller organizations
46
+
47
+ **Admin Consent Override**:
48
+ Organizations can configure policies that require administrator approval even for normally user-consentable permissions:
49
+
50
+ - **Tenant-wide settings**: Admins can disable user consent entirely
51
+ - **Pre-approval**: Admins can grant permissions on behalf of all users
52
+ - **Restricted permissions**: Certain sensitive permissions always require admin consent
53
+
54
+ ### When Admin Consent is Needed
55
+
56
+ 1. **Enterprise Organizations**: Many large companies disable user consent
57
+ 2. **Sensitive Permissions**: Higher-privilege permissions always need admin approval
58
+ 3. **Policy Requirements**: Compliance or security policies may mandate admin review
59
+ 4. **Shared Permissions**: Accessing shared mailboxes/resources (e.g., `Mail.Read.Shared`)
60
+
61
+ ---
62
+
63
+ ## OAuth 2.0 Flow
64
+
65
+ ### 1. Authorization Request
66
+
67
+ **Endpoint**: `https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/authorize`
68
+
69
+ **Parameters**:
70
+
71
+ ```ruby
72
+ {
73
+ client_id: "YOUR_CLIENT_ID",
74
+ redirect_uri: "https://yourdomain.com/oauth/entra_id/callback",
75
+ response_type: "code",
76
+ scope: "openid email profile User.Read Contacts.Read Mail.Send Mail.Read",
77
+ state: "RANDOM_STATE_TOKEN",
78
+ response_mode: "query"
79
+ }
80
+ ```
81
+
82
+ **Optional Parameters**:
83
+
84
+ - `domain_hint`: Pre-fills login with a specific domain (e.g., "contoso.com")
85
+ - `prompt`: Controls the prompt behavior (e.g., "select_account", "consent")
86
+
87
+ ### 2. Token Exchange
88
+
89
+ **Endpoint**: `https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token`
90
+
91
+ **Parameters**:
92
+
93
+ ```ruby
94
+ {
95
+ client_id: "YOUR_CLIENT_ID",
96
+ client_secret: "YOUR_CLIENT_SECRET",
97
+ code: "AUTHORIZATION_CODE",
98
+ redirect_uri: "https://yourdomain.com/oauth/entra_id/callback",
99
+ grant_type: "authorization_code",
100
+ scope: "openid email profile User.Read Contacts.Read Mail.Send Mail.Read"
101
+ }
102
+ ```
103
+
104
+ **Response**:
105
+
106
+ ```json
107
+ {
108
+ "access_token": "eyJ0eXAiOiJKV1QiLCJub25jZSI6...",
109
+ "token_type": "Bearer",
110
+ "expires_in": 3599,
111
+ "scope": "openid email profile User.Read Contacts.Read Mail.Send Mail.Read",
112
+ "refresh_token": "0.AXEA...",
113
+ "id_token": "eyJ0eXAiOiJKV1QiLCJhbGc..."
114
+ }
115
+ ```
116
+
117
+ ### 3. User Info Retrieval
118
+
119
+ **Endpoint**: `https://graph.microsoft.com/v1.0/me`
120
+
121
+ **Response**:
122
+
123
+ ```json
124
+ {
125
+ "id": "48d31887-5fad-4d73-a9f5-3c356e68a038",
126
+ "displayName": "John Doe",
127
+ "mail": "john.doe@contoso.com",
128
+ "userPrincipalName": "john.doe@contoso.com"
129
+ }
130
+ ```
131
+
132
+ **Mapping in Controller**:
133
+
134
+ ```ruby
135
+ {
136
+ id: response[:id] || response[:sub],
137
+ email: response[:mail] || response[:userPrincipalName] || response[:email],
138
+ name: response[:displayName] || response[:name],
139
+ picture: nil
140
+ }
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Configuration
146
+
147
+ ### Application Configuration
148
+
149
+ In your Aven initializer or configuration:
150
+
151
+ ```ruby
152
+ config.oauth_providers = {
153
+ entra_id: {
154
+ client_id: ENV['ENTRA_ID_CLIENT_ID'],
155
+ client_secret: ENV['ENTRA_ID_CLIENT_SECRET'],
156
+ tenant_id: ENV['ENTRA_ID_TENANT_ID'], # or "common" for multi-tenant
157
+ # Optional configurations:
158
+ scope: "openid email profile User.Read Contacts.Read Mail.Send Mail.Read",
159
+ domain_hint: "yourdomain.com", # Pre-fills login domain
160
+ prompt: "select_account" # Forces account selection
161
+ }
162
+ }
163
+ ```
164
+
165
+ ### Tenant ID Options
166
+
167
+ | Tenant ID | Use Case | Behavior |
168
+ | --------------- | ---------------------- | --------------------------------------------------- |
169
+ | `common` | Multi-tenant | Allows any Microsoft account (work/school/personal) |
170
+ | `organizations` | Work/school only | Allows only organizational accounts |
171
+ | `consumers` | Personal accounts only | Allows only personal Microsoft accounts |
172
+ | `{tenant-guid}` | Single tenant | Restricts to specific Azure AD tenant |
173
+
174
+ ### Environment Variables
175
+
176
+ ```bash
177
+ ENTRA_ID_CLIENT_ID=12345678-1234-1234-1234-123456789abc
178
+ ENTRA_ID_CLIENT_SECRET=your_client_secret_value
179
+ ENTRA_ID_TENANT_ID=common
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Azure App Registration Setup
185
+
186
+ ### 1. Create App Registration
187
+
188
+ 1. Go to [Azure Portal](https://portal.azure.com)
189
+ 2. Navigate to **Azure Active Directory** > **App registrations**
190
+ 3. Click **New registration**
191
+ 4. Configure:
192
+ - **Name**: Your application name
193
+ - **Supported account types**:
194
+ - Single tenant (your organization only)
195
+ - Multi-tenant (any organization)
196
+ - Multi-tenant + personal Microsoft accounts
197
+ - **Redirect URI**: `https://yourdomain.com/oauth/entra_id/callback`
198
+
199
+ ### 2. Configure API Permissions
200
+
201
+ 1. Go to **API permissions** in your app registration
202
+ 2. Click **Add a permission**
203
+ 3. Select **Microsoft Graph**
204
+ 4. Select **Delegated permissions**
205
+ 5. Add the following permissions:
206
+ - `User.Read` (usually added by default)
207
+ - `Contacts.Read`
208
+ - `Mail.Send`
209
+ - `Mail.Read`
210
+ 6. (Optional) Click **Grant admin consent** to pre-approve for all users
211
+
212
+ ### 3. Create Client Secret
213
+
214
+ 1. Go to **Certificates & secrets**
215
+ 2. Click **New client secret**
216
+ 3. Add a description and select expiration
217
+ 4. **Copy the secret value immediately** (it won't be shown again)
218
+ 5. Store in `ENTRA_ID_CLIENT_SECRET` environment variable
219
+
220
+ ### 4. Note Your IDs
221
+
222
+ - **Application (client) ID**: Found on Overview page → `ENTRA_ID_CLIENT_ID`
223
+ - **Directory (tenant) ID**: Found on Overview page → `ENTRA_ID_TENANT_ID`
224
+
225
+ ---
226
+
227
+ ## Microsoft Graph API Usage
228
+
229
+ After successful authentication, the `access_token` is stored in `user.access_token` and can be used to call Microsoft Graph APIs.
230
+
231
+ ### Read Contacts
232
+
233
+ ```ruby
234
+ GET https://graph.microsoft.com/v1.0/me/contacts
235
+ Authorization: Bearer {access_token}
236
+ ```
237
+
238
+ **Response**:
239
+
240
+ ```json
241
+ {
242
+ "value": [
243
+ {
244
+ "id": "AAMkAGI2T...",
245
+ "displayName": "Jane Smith",
246
+ "emailAddresses": [
247
+ {
248
+ "address": "jane.smith@example.com",
249
+ "name": "Jane Smith"
250
+ }
251
+ ]
252
+ }
253
+ ]
254
+ }
255
+ ```
256
+
257
+ **Documentation**: https://learn.microsoft.com/en-us/graph/api/user-list-contacts
258
+
259
+ ### Send Email
260
+
261
+ ```ruby
262
+ POST https://graph.microsoft.com/v1.0/me/sendMail
263
+ Authorization: Bearer {access_token}
264
+ Content-Type: application/json
265
+
266
+ {
267
+ "message": {
268
+ "subject": "Hello from Aven",
269
+ "body": {
270
+ "contentType": "Text",
271
+ "content": "This is a test email."
272
+ },
273
+ "toRecipients": [
274
+ {
275
+ "emailAddress": {
276
+ "address": "recipient@example.com"
277
+ }
278
+ }
279
+ ]
280
+ }
281
+ }
282
+ ```
283
+
284
+ **Documentation**: https://learn.microsoft.com/en-us/graph/api/user-sendmail
285
+
286
+ ### Read Email
287
+
288
+ ```ruby
289
+ GET https://graph.microsoft.com/v1.0/me/messages
290
+ Authorization: Bearer {access_token}
291
+ ```
292
+
293
+ **Optional Query Parameters**:
294
+
295
+ - `$select=subject,from,receivedDateTime,bodyPreview`
296
+ - `$filter=isRead eq false`
297
+ - `$orderby=receivedDateTime desc`
298
+ - `$top=10`
299
+
300
+ **Response**:
301
+
302
+ ```json
303
+ {
304
+ "value": [
305
+ {
306
+ "id": "AAMkAGI2T...",
307
+ "subject": "Meeting Tomorrow",
308
+ "from": {
309
+ "emailAddress": {
310
+ "name": "John Doe",
311
+ "address": "john@example.com"
312
+ }
313
+ },
314
+ "receivedDateTime": "2025-10-15T10:30:00Z",
315
+ "bodyPreview": "Just a reminder about our meeting..."
316
+ }
317
+ ]
318
+ }
319
+ ```
320
+
321
+ **Documentation**: https://learn.microsoft.com/en-us/graph/api/user-list-messages
322
+
323
+ ---
324
+
325
+ ## Official Documentation References
326
+
327
+ ### Core OAuth & Authentication
328
+
329
+ 1. **Microsoft Entra ID OAuth 2.0 Authorization Code Flow**
330
+ https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow
331
+
332
+ 2. **Scopes and Permissions in Microsoft Identity Platform**
333
+ https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc
334
+
335
+ 3. **Microsoft Graph Permissions Overview**
336
+ https://learn.microsoft.com/en-us/graph/permissions-overview
337
+
338
+ 4. **Microsoft Graph Permissions Reference (Complete List)**
339
+ https://learn.microsoft.com/en-us/graph/permissions-reference
340
+
341
+ ### API-Specific Documentation
342
+
343
+ 5. **Send Mail API**
344
+ https://learn.microsoft.com/en-us/graph/api/user-sendmail
345
+
346
+ 6. **List Messages API**
347
+ https://learn.microsoft.com/en-us/graph/api/user-list-messages
348
+
349
+ 7. **List Contacts API**
350
+ https://learn.microsoft.com/en-us/graph/api/user-list-contacts
351
+
352
+ 8. **Get Contact API**
353
+ https://learn.microsoft.com/en-us/graph/api/contact-get
354
+
355
+ ### Additional Resources
356
+
357
+ 9. **Graph Permissions Explorer** (Third-party tool)
358
+ https://graphpermissions.merill.net/
359
+
360
+ 10. **Authentication and Authorization Basics**
361
+ https://learn.microsoft.com/en-us/graph/auth/auth-concepts
362
+
363
+ ---
364
+
365
+ ## Implementation Details
366
+
367
+ ### Controller Structure
368
+
369
+ **File**: `app/controllers/aven/oauth/entra_id_controller.rb`
370
+
371
+ **Inherits From**: `Aven::Oauth::BaseController`
372
+
373
+ **Implemented Methods**:
374
+
375
+ - `authorization_url(state)`: Builds the Microsoft authorization URL
376
+ - `exchange_code_for_token(code)`: Exchanges authorization code for access token
377
+ - `fetch_user_info(access_token)`: Retrieves user information from Microsoft Graph
378
+
379
+ **Helper Methods**:
380
+
381
+ - `callback_url`: Generates the OAuth callback URL
382
+ - `oauth_config`: Retrieves Entra ID configuration
383
+ - `tenant_id`: Returns the configured tenant ID (default: "common")
384
+ - `entra_authorization_url`: Microsoft authorization endpoint
385
+ - `entra_token_url`: Microsoft token endpoint
386
+ - `entra_userinfo_url`: Microsoft Graph user info endpoint
387
+
388
+ ### Routes
389
+
390
+ **File**: `config/routes.rb:30-32`
391
+
392
+ ```ruby
393
+ # Microsoft Entra ID OAuth
394
+ get "entra_id", to: "entra_id#create", as: :entra_id
395
+ get "entra_id/callback", to: "entra_id#callback", as: :entra_id_callback
396
+ ```
397
+
398
+ **Available Routes**:
399
+
400
+ - `GET /oauth/entra_id` → Initiates OAuth flow
401
+ - `GET /oauth/entra_id/callback` → Handles OAuth callback
402
+
403
+ **Named Routes**:
404
+
405
+ - `oauth_entra_id_path` → `/oauth/entra_id`
406
+ - `oauth_entra_id_callback_path` → `/oauth/entra_id/callback`
407
+
408
+ ---
409
+
410
+ ## Security Considerations
411
+
412
+ ### State Parameter
413
+
414
+ The implementation uses a secure random state token to prevent CSRF attacks:
415
+
416
+ ```ruby
417
+ state = SecureRandom.hex(16)
418
+ session[:oauth_state] = state
419
+ ```
420
+
421
+ The state is validated in the callback to ensure the response matches the request.
422
+
423
+ ### Token Storage
424
+
425
+ Access tokens are stored in the `Aven::User` model:
426
+
427
+ ```ruby
428
+ user.access_token = token_data[:access_token]
429
+ ```
430
+
431
+ **Security Recommendations**:
432
+
433
+ 1. Encrypt the `access_token` column in the database
434
+ 2. Implement token refresh logic (access tokens expire)
435
+ 3. Store `refresh_token` separately for long-term access
436
+ 4. Use HTTPS for all OAuth endpoints
437
+
438
+ ### HTTPS Requirement
439
+
440
+ OAuth 2.0 requires HTTPS for security. The implementation includes SSL verification:
441
+
442
+ ```ruby
443
+ http.use_ssl = true
444
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if Rails.env.development?
445
+ ```
446
+
447
+ **Production**: Always use proper SSL certificates.
448
+
449
+ ---
450
+
451
+ ## Troubleshooting
452
+
453
+ ### Common Issues
454
+
455
+ #### 1. "Invalid redirect_uri"
456
+
457
+ **Cause**: Redirect URI mismatch between your app and Azure configuration.
458
+
459
+ **Solution**: Ensure the redirect URI in Azure matches exactly:
460
+
461
+ ```
462
+ https://yourdomain.com/oauth/entra_id/callback
463
+ ```
464
+
465
+ #### 2. "AADSTS65001: The user or administrator has not consented"
466
+
467
+ **Cause**: User hasn't consented to the requested permissions.
468
+
469
+ **Solution**:
470
+
471
+ - Have admin grant consent in Azure portal
472
+ - Or adjust scopes to user-consentable permissions only
473
+
474
+ #### 3. "ErrorAccessDenied" when calling Graph API
475
+
476
+ **Cause**: Missing permissions or token scope mismatch.
477
+
478
+ **Solution**:
479
+
480
+ 1. Verify permissions are added in Azure portal
481
+ 2. Check the scope parameter includes all needed permissions
482
+ 3. Request a new token after adding permissions
483
+
484
+ #### 4. "Invalid client secret"
485
+
486
+ **Cause**: Client secret expired or incorrect.
487
+
488
+ **Solution**:
489
+
490
+ 1. Generate a new client secret in Azure portal
491
+ 2. Update `ENTRA_ID_CLIENT_SECRET` environment variable
492
+ 3. Note: Secrets expire (check expiration date)
493
+
494
+ ### Debug Mode
495
+
496
+ In development, detailed errors are shown:
497
+
498
+ ```ruby
499
+ error_message = if Rails.env.production?
500
+ "Authentication failed. Please try again."
501
+ else
502
+ "#{e.message}"
503
+ end
504
+ ```
505
+
506
+ Check Rails logs for full error details.
507
+
508
+ ---
509
+
510
+ ## Token Refresh (Future Enhancement)
511
+
512
+ Currently, the implementation stores only the access token. For long-term access, implement refresh token logic:
513
+
514
+ ```ruby
515
+ def refresh_access_token(refresh_token)
516
+ params = {
517
+ client_id: oauth_config[:client_id],
518
+ client_secret: oauth_config[:client_secret],
519
+ refresh_token: refresh_token,
520
+ grant_type: "refresh_token"
521
+ }
522
+
523
+ oauth_request(URI(entra_token_url), params)
524
+ end
525
+ ```
526
+
527
+ Store the `refresh_token` from the initial token response and use it to obtain new access tokens when they expire (typically after 1 hour).
528
+
529
+ ---
530
+
531
+ ## Testing
532
+
533
+ ### Manual Testing
534
+
535
+ 1. Navigate to: `http://localhost:3000/oauth/entra_id`
536
+ 2. Sign in with a Microsoft account
537
+ 3. Grant permissions when prompted
538
+ 4. Verify redirect back to your application
539
+ 5. Check user record has `access_token` populated
540
+
541
+ ### Test Accounts
542
+
543
+ For development, create test users in your Azure AD tenant:
544
+
545
+ - Go to **Azure Active Directory** > **Users**
546
+ - Create test users with various permission levels
547
+
548
+ ### GraphQL Explorer
549
+
550
+ Test Graph API calls using Microsoft's Graph Explorer:
551
+ https://developer.microsoft.com/en-us/graph/graph-explorer
552
+
553
+ ---
554
+
555
+ ## Additional Features to Consider
556
+
557
+ ### 1. Profile Picture Support
558
+
559
+ Microsoft Graph supports retrieving user photos:
560
+
561
+ ```ruby
562
+ GET https://graph.microsoft.com/v1.0/me/photo/$value
563
+ Authorization: Bearer {access_token}
564
+ ```
565
+
566
+ Update `fetch_user_info` to include picture URL.
567
+
568
+ ### 2. Offline Access
569
+
570
+ Add `offline_access` scope to receive refresh tokens:
571
+
572
+ ```ruby
573
+ DEFAULT_SCOPE = "openid email profile offline_access User.Read Contacts.Read Mail.Send Mail.Read"
574
+ ```
575
+
576
+ ### 3. Calendar Access
577
+
578
+ Add calendar permissions:
579
+
580
+ - `Calendars.Read`: Read user's calendar
581
+ - `Calendars.ReadWrite`: Full calendar access
582
+
583
+ ### 4. OneDrive Access
584
+
585
+ Add file storage permissions:
586
+
587
+ - `Files.Read`: Read user's files
588
+ - `Files.ReadWrite`: Full file access
589
+
590
+ ---
591
+
592
+ ## Version History
593
+
594
+ - **v1.0** (2025-10-15): Initial implementation with authentication, contacts, and email support
595
+
596
+ ---
597
+
598
+ ## Support & Resources
599
+
600
+ - **Microsoft Graph API Documentation**: https://learn.microsoft.com/en-us/graph/
601
+ - **Azure Portal**: https://portal.azure.com
602
+ - **Microsoft Q&A**: https://learn.microsoft.com/en-us/answers/
603
+ - **Stack Overflow**: Tag with `microsoft-graph` and `azure-active-directory`
604
+
605
+ ---
606
+
607
+ **Last Updated**: October 15, 2025
608
+ **Maintained By**: Aven Development Team