Skip to content

Commit 6e1b8f9

Browse files
Merge pull request #6 from mauriciozanettisalomao/feat/lfxv2-587-user-metadata-read-auth0
[LFXV2-587] User metadata READ - Auth0
2 parents 8902ade + 52bab55 commit 6e1b8f9

File tree

17 files changed

+876
-59
lines changed

17 files changed

+876
-59
lines changed

README.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,117 @@ nats request lfx.auth-service.email_to_username zephyr.stormwind@mythicaltech.io
9292

9393
---
9494

95+
### User Metadata Retrieval
96+
97+
To retrieve user metadata, send a NATS request to the following subject:
98+
99+
**Subject:** `lfx.auth-service.user_metadata.read`
100+
**Pattern:** Request/Reply
101+
102+
The service supports two lookup strategies based on the input format, providing both authoritative identification and convenient username-based searches.
103+
104+
##### Input Format and Strategy Selection
105+
106+
The service automatically determines the lookup strategy based on the input format:
107+
108+
- **Canonical Lookup** (contains `|`): `<connection>|<provider_user_id>` - Subject identifier
109+
- **Search Lookup** (no `|`): `<username>` - Convenience lookup
110+
111+
##### Canonical Lookup Strategy (Recommended)
112+
113+
**Format:** `<connection>|<provider_user_id>`
114+
115+
The canonical lookup is the **authoritative, standard way to identify a user**, regardless of which provider they come from.
116+
117+
**Examples:** `auth0|123456789`, `google-oauth2|987654321`, `samlp|my-connection|user123`
118+
119+
##### Search Lookup Strategy (Convenience)
120+
121+
**Format:** `<username>`
122+
123+
Username lookups are **convenience only** and help avoid connection collisions within the Username-Password-Authentication connection.
124+
125+
**Examples:** `john.doe`, `jane.smith`, `developer123`
126+
127+
##### Request Payload
128+
129+
The request payload should be a plain text identifier (no JSON wrapping required):
130+
131+
**Canonical Lookup:**
132+
```
133+
auth0|123456789
134+
```
135+
136+
**Search Lookup:**
137+
```
138+
john.doe
139+
```
140+
141+
##### Reply
142+
143+
The service returns a structured reply with user metadata:
144+
145+
**Success Reply:**
146+
```json
147+
{
148+
"success": true,
149+
"data": {
150+
"name": "John Doe",
151+
"given_name": "John",
152+
"family_name": "Doe",
153+
"job_title": "Software Engineer",
154+
"organization": "Example Corp",
155+
"country": "United States",
156+
"state_province": "California",
157+
"city": "San Francisco",
158+
"address": "123 Main Street",
159+
"postal_code": "94102",
160+
"phone_number": "+1-555-0123",
161+
"t_shirt_size": "L",
162+
"picture": "https://example.com/avatar.jpg",
163+
"zoneinfo": "America/Los_Angeles"
164+
}
165+
}
166+
```
167+
168+
**Error Reply (User Not Found):**
169+
```json
170+
{
171+
"success": false,
172+
"error": "user not found"
173+
}
174+
```
175+
176+
**Error Reply (Invalid Input):**
177+
```json
178+
{
179+
"success": false,
180+
"error": "input is required"
181+
}
182+
```
183+
184+
##### Examples using NATS CLI
185+
186+
```bash
187+
# Canonical lookup (subject identifier)
188+
# Note: Use quotes to escape the pipe character in shell commands
189+
nats request lfx.auth-service.user_metadata.read "auth0|123456789"
190+
191+
# Search lookup (convenience username lookup)
192+
nats request lfx.auth-service.user_metadata.read john.doe
193+
194+
```
195+
196+
**Important Notes:**
197+
- **Canonical lookups** are the preferred method for system-to-system communication
198+
- **Search lookups** are provided for convenience and user-facing interfaces
199+
- The pipe character (`|`) in canonical identifiers must be escaped with quotes in shell commands
200+
- Both strategies return the same metadata format on success
201+
- When using mock or authelia mode, the service simulates Auth0 behavior for development and testing
202+
- For detailed Auth0-specific behavior and limitations, see the [Auth0 Integration Documentation](internal/infrastructure/auth0/README.md)
203+
204+
---
205+
95206
### User Update Operation
96207

97208
To update a user profile, send a NATS request to the following subject:

charts/lfx-v2-auth-service/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ apiVersion: v2
55
name: lfx-v2-auth-service
66
description: LFX Platform V2 Auth Service chart
77
type: application
8-
version: 0.2.0
8+
version: 0.2.1
99
appVersion: "latest"

cmd/server/service/message_handler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func (mhs *MessageHandlerService) HandleMessage(ctx context.Context, msg port.Tr
2828
handlers := map[string]func(ctx context.Context, msg port.TransportMessenger) ([]byte, error){
2929
constants.UserMetadataUpdateSubject: mhs.messageHandler.UpdateUser,
3030
constants.UserEmailToUserSubject: mhs.messageHandler.EmailToUsername,
31+
constants.UserMetadataReadSubject: mhs.messageHandler.GetUserMetadata,
3132
}
3233

3334
handler, ok := handlers[subject]

cmd/server/service/providers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ func QueueSubscriptions(ctx context.Context) error {
160160
subjects := map[string]func(context.Context, port.TransportMessenger){
161161
constants.UserMetadataUpdateSubject: messageHandlerService.HandleMessage,
162162
constants.UserEmailToUserSubject: messageHandlerService.HandleMessage,
163+
constants.UserMetadataReadSubject: messageHandlerService.HandleMessage,
163164
// Add more subjects here as needed
164165
}
165166

internal/domain/model/auth0.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,46 @@ type Auth0Identity struct {
2323
IsSocial bool `json:"isSocial"`
2424
}
2525

26-
// Auth0UserMetadata represents the metadata of a user in Auth0
26+
// Auth0UserMetadata represents the metadata of a user in Auth0.
27+
//
28+
// It's the same as the domain User.UserMetadata, but it might be useful
29+
// to have it separated for Auth0 to handle it separately if needed.
2730
type Auth0UserMetadata struct {
28-
Name *string `json:"name"`
29-
FamilyName *string `json:"family_name"`
30-
GivenName *string `json:"given_name"`
31-
Picture *string `json:"picture"`
32-
JobTitle *string `json:"job_title"`
31+
Name *string `json:"name"`
32+
FamilyName *string `json:"family_name"`
33+
GivenName *string `json:"given_name"`
34+
Picture *string `json:"picture"`
35+
JobTitle *string `json:"job_title"`
36+
Organization *string `json:"organization"`
37+
Country *string `json:"country"`
38+
StateProvince *string `json:"state_province"`
39+
City *string `json:"city"`
40+
Address *string `json:"address"`
41+
PostalCode *string `json:"postal_code"`
42+
PhoneNumber *string `json:"phone_number"`
43+
TShirtSize *string `json:"t_shirt_size"`
44+
Zoneinfo *string `json:"zoneinfo"`
3345
}
3446

3547
// ToUser converts an Auth0User to a User
3648
func (u *Auth0User) ToUser() *User {
3749
var meta *UserMetadata
3850
if u.UserMetadata != nil {
3951
meta = &UserMetadata{
40-
Name: u.UserMetadata.Name,
41-
FamilyName: u.UserMetadata.FamilyName,
42-
GivenName: u.UserMetadata.GivenName,
43-
Picture: u.UserMetadata.Picture,
44-
JobTitle: u.UserMetadata.JobTitle,
52+
Name: u.UserMetadata.Name,
53+
FamilyName: u.UserMetadata.FamilyName,
54+
GivenName: u.UserMetadata.GivenName,
55+
Picture: u.UserMetadata.Picture,
56+
JobTitle: u.UserMetadata.JobTitle,
57+
Organization: u.UserMetadata.Organization,
58+
Country: u.UserMetadata.Country,
59+
StateProvince: u.UserMetadata.StateProvince,
60+
City: u.UserMetadata.City,
61+
Address: u.UserMetadata.Address,
62+
PostalCode: u.UserMetadata.PostalCode,
63+
PhoneNumber: u.UserMetadata.PhoneNumber,
64+
TShirtSize: u.UserMetadata.TShirtSize,
65+
Zoneinfo: u.UserMetadata.Zoneinfo,
4566
}
4667
}
4768
return &User{

internal/domain/model/user.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
type User struct {
1515
Token string `json:"token" yaml:"token"`
1616
UserID string `json:"user_id" yaml:"user_id"`
17+
Sub string `json:"sub,omitempty" yaml:"sub,omitempty"`
1718
Username string `json:"username" yaml:"username"`
1819
PrimaryEmail string `json:"primary_email" yaml:"primary_email"`
1920
UserMetadata *UserMetadata `json:"user_metadata,omitempty" yaml:"user_metadata,omitempty"`
@@ -60,6 +61,7 @@ func (u *User) UserSanitize() {
6061
// Sanitize basic user fields
6162
u.Token = strings.TrimSpace(u.Token)
6263
u.UserID = strings.TrimSpace(u.UserID)
64+
u.Sub = strings.TrimSpace(u.Sub)
6365
u.Username = strings.TrimSpace(u.Username)
6466
u.PrimaryEmail = strings.TrimSpace(u.PrimaryEmail)
6567

@@ -116,3 +118,22 @@ func (um *UserMetadata) userMetadataSanitize() {
116118
*um.Zoneinfo = strings.TrimSpace(*um.Zoneinfo)
117119
}
118120
}
121+
122+
// PrepareForMetadataLookup prepares the user for metadata lookup based on the input
123+
// Returns true if should use canonical lookup, false if should use search
124+
func (u *User) PrepareForMetadataLookup(input string) bool {
125+
input = strings.TrimSpace(input)
126+
127+
if strings.Contains(input, "|") {
128+
// Input contains "|", use as sub for canonical lookup
129+
u.Sub = input
130+
u.UserID = input // Auth0 uses user_id for the canonical lookup
131+
u.Username = "" // Clear username field
132+
return true
133+
}
134+
// Input doesn't contain "|", use for search query
135+
u.Sub = "" // Clear sub field
136+
u.UserID = "" // Clear user_id field
137+
u.Username = input
138+
return false
139+
}

0 commit comments

Comments
 (0)