Skip to content

Commit 5ff7369

Browse files
committed
feat: add marketing consent support to sdk and prefab
1 parent 65cf736 commit 5ff7369

File tree

8 files changed

+162
-28
lines changed

8 files changed

+162
-28
lines changed

src/Packages/Passport/Runtime/Scripts/Private/Helpers/WindowsDeepLink.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,6 @@ private static void RegisterProtocol(string protocolName)
259259

260260
// Set command to launch the script with the URI parameter
261261
var scriptLocation = GetGameExecutablePath(".cmd");
262-
//string command = $"cmd.exe /c \"\"{scriptLocation}\" \"%1\"\"";
263262
string command = $"\"{scriptLocation}\" \"%1\"";
264263
uint commandSize = (uint)((command.Length + 1) * 2);
265264

src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginMethod.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace Immutable.Passport.Model
88
[Serializable]
99
public enum DirectLoginMethod
1010
{
11+
None,
1112
Email,
1213
Google,
1314
Apple,

src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,32 @@ public class DirectLoginOptions
1919
/// </summary>
2020
public string email;
2121

22+
/// <summary>
23+
/// Marketing consent status (optional).
24+
/// </summary>
25+
public MarketingConsentStatus? marketingConsentStatus;
26+
2227
/// <summary>
2328
/// Default constructor.
2429
/// </summary>
2530
public DirectLoginOptions()
2631
{
2732
directLoginMethod = DirectLoginMethod.Email;
2833
email = null;
34+
marketingConsentStatus = null;
2935
}
3036

3137
/// <summary>
3238
/// Constructor with method and email.
3339
/// </summary>
3440
/// <param name="loginMethod">The direct login method</param>
3541
/// <param name="emailAddress">The email address (optional)</param>
36-
public DirectLoginOptions(DirectLoginMethod loginMethod, string emailAddress = null)
42+
/// <param name="marketingConsentStatus">The marketing consent status (optional)</param>
43+
public DirectLoginOptions(DirectLoginMethod loginMethod, string emailAddress = null, MarketingConsentStatus? marketingConsentStatus = null)
3744
{
3845
directLoginMethod = loginMethod;
3946
email = emailAddress;
47+
this.marketingConsentStatus = marketingConsentStatus;
4048
}
4149

4250
/// <summary>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
3+
namespace Immutable.Passport.Model
4+
{
5+
/// <summary>
6+
/// Enum representing marketing consent status.
7+
/// </summary>
8+
[Serializable]
9+
public enum MarketingConsentStatus
10+
{
11+
OptedIn,
12+
Unsubscribed
13+
}
14+
15+
/// <summary>
16+
/// Extension methods for MarketingConsentStatus enum.
17+
/// </summary>
18+
public static class MarketingConsentStatusExtensions
19+
{
20+
/// <summary>
21+
/// Converts the enum value to the string format expected by the game bridge.
22+
/// </summary>
23+
/// <param name="status">The marketing consent status</param>
24+
/// <returns>The corresponding string value</returns>
25+
public static string ToApiString(this MarketingConsentStatus status)
26+
{
27+
return status switch
28+
{
29+
MarketingConsentStatus.OptedIn => "opted_in",
30+
MarketingConsentStatus.Unsubscribed => "unsubscribed",
31+
_ => throw new ArgumentOutOfRangeException(nameof(status), status, "Unknown MarketingConsentStatus value")
32+
};
33+
}
34+
}
35+
}

src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,22 @@ private async UniTask LaunchAuthUrl()
287287
requestJson += $",\"email\":\"{_directLoginOptions.email}\"";
288288
}
289289

290+
if (_directLoginOptions.marketingConsentStatus != null)
291+
{
292+
var consentValue = _directLoginOptions.marketingConsentStatus.Value.ToApiString();
293+
requestJson += $",\"marketingConsentStatus\":\"{consentValue}\"";
294+
}
295+
290296
requestJson += "}";
291297
}
298+
else
299+
{
300+
PassportLogger.Debug($"{TAG} No direct login options provided (standard auth flow)");
301+
}
292302

293303
requestJson += "}";
294-
304+
305+
PassportLogger.Debug($"{TAG} Sending auth URL request: {requestJson}");
295306
var callResponse = await _communicationsManager.Call(PassportFunction.GET_PKCE_AUTH_URL, requestJson);
296307
var response = callResponse.OptDeserializeObject<StringResponse>();
297308

src/Packages/Passport/Runtime/Scripts/Public/PassportManager.cs

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class PassportManager : MonoBehaviour
3232
[SerializeField] private bool autoInitialize = true;
3333
[SerializeField] private bool autoLogin = false;
3434
[SerializeField] private DirectLoginMethod directLoginMethod = DirectLoginMethod.None;
35+
[SerializeField] private MarketingConsentStatus defaultMarketingConsent = MarketingConsentStatus.Unsubscribed;
3536
[SerializeField] private LogLevel logLevel = LogLevel.Info;
3637
[SerializeField] private bool redactTokensInLogs = true;
3738

@@ -206,19 +207,19 @@ public async void Login()
206207
}
207208

208209
/// <summary>
209-
/// Login with a specific direct login method
210+
/// Login with custom direct login options
210211
/// </summary>
211-
/// <param name="loginMethod">The login method to use (Google, Apple, Facebook, or None for default)</param>
212-
public async void Login(DirectLoginMethod loginMethod)
212+
/// <param name="directLoginOptions">Custom direct login options including method, email, and marketing consent</param>
213+
public async void Login(DirectLoginOptions directLoginOptions)
213214
{
214-
await LoginAsync(loginMethod);
215+
await LoginAsync(directLoginOptions: directLoginOptions);
215216
}
216217

217218
/// <summary>
218219
/// Internal async login method
219220
/// </summary>
220-
/// <param name="loginMethod">Optional login method override. If not provided, uses the configured directLoginMethod</param>
221-
private async UniTask LoginAsync(DirectLoginMethod? loginMethod = null)
221+
/// <param name="directLoginOptions">Optional direct login options. If null, uses the configured directLoginMethod</param>
222+
private async UniTask LoginAsync(DirectLoginOptions directLoginOptions = null)
222223
{
223224
if (!IsInitialized || PassportInstance == null)
224225
{
@@ -228,13 +229,44 @@ private async UniTask LoginAsync(DirectLoginMethod? loginMethod = null)
228229

229230
try
230231
{
231-
DirectLoginMethod methodToUse = loginMethod ?? directLoginMethod;
232-
string loginMethodText = methodToUse == DirectLoginMethod.None
233-
? "default method"
234-
: methodToUse.ToString();
232+
// Determine final DirectLoginOptions to use
233+
DirectLoginOptions finalDirectLoginOptions;
234+
string loginMethodText;
235+
236+
if (directLoginOptions != null)
237+
{
238+
// Use provided DirectLoginOptions (marketing consent already set by developer)
239+
finalDirectLoginOptions = directLoginOptions;
240+
loginMethodText = directLoginOptions.directLoginMethod.ToString();
241+
}
242+
else
243+
{
244+
// Use configured directLoginMethod from Inspector
245+
loginMethodText = directLoginMethod == DirectLoginMethod.None
246+
? "default method"
247+
: directLoginMethod.ToString();
248+
249+
if (directLoginMethod == DirectLoginMethod.None)
250+
{
251+
// Standard auth flow
252+
finalDirectLoginOptions = null;
253+
}
254+
else
255+
{
256+
// Direct login with configured default marketing consent
257+
finalDirectLoginOptions = new DirectLoginOptions(directLoginMethod, marketingConsentStatus: defaultMarketingConsent);
258+
}
259+
}
260+
235261
Debug.Log($"[PassportManager] Attempting login with {loginMethodText}...");
236262

237-
bool loginSuccess = await PassportInstance.Login(useCachedSession: false, directLoginMethod: methodToUse);
263+
// Debug log marketing consent if present
264+
if (finalDirectLoginOptions?.marketingConsentStatus != null)
265+
{
266+
Debug.Log($"[PassportManager] Marketing consent: {finalDirectLoginOptions.marketingConsentStatus}");
267+
}
268+
269+
bool loginSuccess = await PassportInstance.Login(useCachedSession: false, directLoginOptions: finalDirectLoginOptions);
238270
if (loginSuccess)
239271
{
240272
IsLoggedIn = true;
@@ -324,23 +356,23 @@ private void ConfigureUIElements()
324356
if (googleLoginButton != null)
325357
{
326358
googleLoginButton.onClick.RemoveAllListeners();
327-
googleLoginButton.onClick.AddListener(() => Login(DirectLoginMethod.Google));
359+
googleLoginButton.onClick.AddListener(() => Login(new DirectLoginOptions(DirectLoginMethod.Google, marketingConsentStatus: defaultMarketingConsent)));
328360
googleLoginButton.interactable = IsInitialized && !IsLoggedIn;
329-
Debug.Log("[PassportManager] Configured Google login button");
361+
Debug.Log($"[PassportManager] Configured Google login button with defaultMarketingConsent: {defaultMarketingConsent}");
330362
}
331363

332364
if (appleLoginButton != null)
333365
{
334366
appleLoginButton.onClick.RemoveAllListeners();
335-
appleLoginButton.onClick.AddListener(() => Login(DirectLoginMethod.Apple));
367+
appleLoginButton.onClick.AddListener(() => Login(new DirectLoginOptions(DirectLoginMethod.Apple, marketingConsentStatus: defaultMarketingConsent)));
336368
appleLoginButton.interactable = IsInitialized && !IsLoggedIn;
337369
Debug.Log("[PassportManager] Configured Apple login button");
338370
}
339371

340372
if (facebookLoginButton != null)
341373
{
342374
facebookLoginButton.onClick.RemoveAllListeners();
343-
facebookLoginButton.onClick.AddListener(() => Login(DirectLoginMethod.Facebook));
375+
facebookLoginButton.onClick.AddListener(() => Login(new DirectLoginOptions(DirectLoginMethod.Facebook, marketingConsentStatus: defaultMarketingConsent)));
344376
facebookLoginButton.interactable = IsInitialized && !IsLoggedIn;
345377
Debug.Log("[PassportManager] Configured Facebook login button");
346378
}

src/Packages/Passport/Samples~/PassportManagerPrefab/README.md

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ The **PassportManager** provides multiple drag-and-drop prefabs for easy Immutab
5757
- **Auto Initialize**: Automatically initialize on Start (default: true)
5858
- **Auto Login**: Automatically attempt login after initialization (default: false)
5959
- **Direct Login Method**: Pre-select login method (None, Google, Apple, Facebook)
60+
- **Default Marketing Consent**: Default consent status for marketing communications (Unsubscribed, OptedIn)
6061
- **Log Level**: Control debug output verbosity
6162

6263
### UI Customization (PassportManagerComplete)
@@ -149,13 +150,57 @@ if (manager.IsInitialized && manager.IsLoggedIn)
149150
// var address = await manager.PassportInstance.GetAddress();
150151
}
151152

152-
// Manual login with specific method
153-
manager.Login(DirectLoginMethod.Google);
153+
// Manual login with specific method and marketing consent
154+
var loginOptions = new DirectLoginOptions(DirectLoginMethod.Google, marketingConsentStatus: MarketingConsentStatus.Unsubscribed);
155+
manager.Login(loginOptions);
156+
157+
// Or use the simpler method (uses default marketing consent from Inspector)
158+
manager.Login(); // Uses configured directLoginMethod
154159
155160
// Manual logout
156161
manager.Logout();
157162
```
158163

164+
## 📧 Marketing Consent
165+
166+
The PassportManager supports marketing consent for compliance with privacy regulations (GDPR, etc.):
167+
168+
### Default Marketing Consent
169+
170+
Configure the default marketing consent status in the Inspector:
171+
172+
- **Unsubscribed** (default): Users are not subscribed to marketing communications by default
173+
- **OptedIn**: Users are opted into marketing communications by default
174+
175+
This setting is used when:
176+
177+
- Users authenticate via direct login methods (Google, Apple, Facebook)
178+
- No explicit marketing consent is provided in code
179+
180+
### Advanced Marketing Consent
181+
182+
For programmatic control over marketing consent:
183+
184+
```csharp
185+
// Create login options with specific marketing consent
186+
var loginOptions = new DirectLoginOptions(
187+
DirectLoginMethod.Google,
188+
marketingConsentStatus: MarketingConsentStatus.Unsubscribed
189+
);
190+
191+
// Login with explicit marketing consent
192+
await PassportManager.Instance.LoginAsync(loginOptions);
193+
```
194+
195+
### Marketing Consent Flow
196+
197+
1. **Unity Game**: Sets marketing consent via `DirectLoginOptions` or uses default from Inspector
198+
2. **Authentication Server**: Receives and processes the marketing consent preference
199+
3. **User Profile**: Marketing consent status is stored and applied to user's profile
200+
4. **Compliance**: Ensures proper consent handling for marketing communications
201+
202+
**Note**: Marketing consent is automatically handled during the authentication flow and integrated with Immutable's user profile system.
203+
159204
## 📁 Sample Scripts
160205

161206
Check out the included example script:
@@ -182,14 +227,6 @@ The `PassportUIController` uses **aggressive cursor management** designed for de
182227
}
183228
```
184229

185-
## 🔗 Deep Linking Setup
186-
187-
For native platforms (Windows/Mac), you'll need to register your custom URL scheme:
188-
189-
### Windows
190-
191-
The SDK automatically handles Windows deep linking registration.
192-
193230
## 🆘 Troubleshooting
194231

195232
### "Can't drag UI elements to Inspector fields"

0 commit comments

Comments
 (0)