Skip to content

Commit 343c94f

Browse files
authored
Add user invite events to support user invite authorisation (#624)
* Update user invit models for authorisation. * Adding authorisation staus model. * Added user invite event types. * Added user invite details endpoint client. * Do user invite authorisation check first (added DB script to set all existing invites as authorised).
1 parent 52c917c commit 343c94f

File tree

5 files changed

+153
-6
lines changed

5 files changed

+153
-6
lines changed

src/NoFrixion.MoneyMoov/ApiClients/UserInviteClient.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public interface IUserInviteClient
2626

2727
Task<RestApiResponse<UserInvite>> GetUserInviteAsync(string accessToken, Guid userInviteID);
2828

29+
Task<RestApiResponse<UserInvite>> GetUserInviteDetailsAsync(string accessToken, Guid userInviteID);
30+
2931
Task<RestApiResponse<UserInvite>> SendInviteAsync(string userAccessToken, Guid merchantID,
3032
string inviteeEmailAddress,
3133
string inviteRegistrationUrl,
@@ -39,6 +41,8 @@ Task<RestApiResponse<UserInvite>> SendInviteAsync(string userAccessToken, Guid m
3941
Task<RestApiResponse> ResendUserInviteAsync(string accessToken, Guid userInviteID);
4042

4143
Task<RestApiResponse> DeleteUserInviteAsync(string accessToken, Guid inviteID);
44+
45+
Task<RestApiResponse> AuthoriseUserInviteAsync(string strongUserAccessToken, Guid inviteID);
4246
}
4347

4448
public class UserInviteClient : IUserInviteClient
@@ -102,6 +106,25 @@ public Task<RestApiResponse<UserInvite>> GetUserInviteAsync(string accessToken,
102106
};
103107
}
104108

109+
/// <summary>
110+
/// Calls the MoneyMoov Merchant get user invite details endpoint to get a single user invite by ID.
111+
/// </summary>
112+
/// <param name="accessToken">A User scoped JWT access token.</param>
113+
/// <param name="userInviteID">The ID of the user invite to retrieve.</param>
114+
/// <returns>If successful, a user invite object.</returns>
115+
public Task<RestApiResponse<UserInvite>> GetUserInviteDetailsAsync(string accessToken, Guid userInviteID)
116+
{
117+
var url = MoneyMoovUrlBuilder.UserInvitesApi.UserInviteDetailsUrl(_apiClient.GetBaseUri().ToString(), userInviteID);
118+
119+
var prob = _apiClient.CheckAccessToken(accessToken, nameof(GetUserInviteDetailsAsync));
120+
121+
return prob switch
122+
{
123+
var p when p.IsEmpty => _apiClient.GetAsync<UserInvite>(url, accessToken),
124+
_ => Task.FromResult(new RestApiResponse<UserInvite>(HttpStatusCode.PreconditionFailed, new Uri(url), prob))
125+
};
126+
}
127+
105128
/// <summary>
106129
/// Calls the MoneyMoov account endpoint to create and send an email invite to register
107130
/// and join a merchant account.
@@ -192,4 +215,25 @@ public Task<RestApiResponse> DeleteUserInviteAsync(string accessToken, Guid invi
192215
_ => Task.FromResult(new RestApiResponse(HttpStatusCode.PreconditionFailed, new Uri(url), prob))
193216
};
194217
}
218+
219+
/// <summary>
220+
/// Calls the MoneyMoov user invites endpoint to authorise a user invite.
221+
/// </summary>
222+
/// <param name="strongUserAccessToken">The strong user access token acquired to authorise the user invite. Strong
223+
/// tokens can only be acquired from a strong customer authentication flow, are short lived (typically 5 minute expiry)
224+
/// and are specific to the user invite.</param>
225+
/// <param name="inviteID">The ID of the user invite to authorise.</param>
226+
/// <returns>An API response indicating the result of the authorise attempt.</returns>
227+
public Task<RestApiResponse> AuthoriseUserInviteAsync(string strongUserAccessToken, Guid inviteID)
228+
{
229+
var url = MoneyMoovUrlBuilder.UserInvitesApi.AuthoriseUserInviteUrl(_apiClient.GetBaseUri().ToString(), inviteID);
230+
231+
var prob = _apiClient.CheckAccessToken(strongUserAccessToken, nameof(AuthoriseUserInviteAsync));
232+
233+
return prob switch
234+
{
235+
var p when p.IsEmpty => _apiClient.PostAsync(url, strongUserAccessToken),
236+
_ => Task.FromResult(new RestApiResponse(HttpStatusCode.PreconditionFailed, new Uri(url), prob))
237+
};
238+
}
195239
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//-----------------------------------------------------------------------------
2+
// Filename: UserInviteEventTypeEnum.cs
3+
//
4+
// Description: Enum for the different types of user invite events that can
5+
// occur.
6+
//
7+
// Author(s):
8+
// Aaron Clauson ([email protected])
9+
//
10+
// History:
11+
// 06 Aug 2025 Aaron Clauson Created, Carnesore Point, Wexford, Ireland.
12+
//
13+
// License:
14+
// MIT.
15+
//-----------------------------------------------------------------------------
16+
17+
namespace NoFrixion.MoneyMoov.Enums;
18+
19+
public enum UserInviteEventTypeEnum
20+
{
21+
/// <summary>
22+
/// Something went wrong and the event type is unknown.
23+
/// </summary>
24+
Unknown = 0,
25+
26+
/// <summary>
27+
/// A user invite was authorised by an approver.
28+
/// </summary>
29+
Authorise = 1
30+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//-----------------------------------------------------------------------------
2+
// Filename: AuthorisationStatus.cs
3+
//
4+
// Description: Represents the authorisation status for a parent model,
5+
// e.g. payout, beneficiary etc. Once the parent model is authorised this state
6+
// can largely be ignored until the next time the parent model is updated.
7+
//
8+
// Author(s):
9+
// Aaron Clauson ([email protected])
10+
//
11+
// History:
12+
// 02 Aug 2025 Aaron Clauson Created, Carnesore Point, Wexford, Ireland.
13+
//
14+
// License:
15+
// MIT.
16+
//-----------------------------------------------------------------------------
17+
18+
using NoFrixion.MoneyMoov.Enums;
19+
using NoFrixion.MoneyMoov.Models.Approve;
20+
21+
namespace NoFrixion.MoneyMoov.Models;
22+
23+
public class AuthorisationStatus
24+
{
25+
/// <summary>
26+
/// A list of users who have successfully authorised the latest version of the parent model.
27+
/// </summary>
28+
public List<Authorisation>? Authorisations { get; set; }
29+
30+
/// <summary>
31+
/// True if the parent model can be authorised by the user who loaded it.
32+
/// </summary>
33+
public bool CanAuthorise { get; set; }
34+
35+
/// <summary>
36+
/// True if the parent model can be updated by the user who loaded it.
37+
/// </summary>
38+
public bool CanUpdate { get; set; }
39+
40+
/// <summary>
41+
/// True if the parent model was loaded for a user and that user has already authorised the latest version.
42+
/// </summary>
43+
public bool HasCurrentUserAuthorised { get; set; }
44+
45+
/// <summary>
46+
/// The number of authorisers required for the parent model. Is determined by business settings
47+
/// on the source account and/or merchant.
48+
/// </summary>
49+
public int AuthorisersRequiredCount { get; set; }
50+
51+
/// <summary>
52+
/// The number of distinct authorisers that have authorised the parent model.
53+
/// </summary>
54+
public int AuthorisersCompletedCount { get; set; }
55+
56+
/// <summary>
57+
/// A list of authentication types allowed to authorise the parent model.
58+
/// </summary>
59+
public List<AuthenticationTypesEnum>? AuthenticationMethods { get; set; }
60+
}

src/NoFrixion.MoneyMoov/Models/UserInvite/UserInvite.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,26 @@ public class UserInvite
5555
/// </summary>
5656
public bool IsAuthorised { get; set; }
5757

58+
/// <summary>
59+
/// The authorisation status for the user invite. It is used for invites
60+
/// that require authorisation before they can be activated.
61+
/// </summary>
62+
public AuthorisationStatus? AuthorisationStatus { get; set; }
63+
5864
public UserInviteStatusEnum Status
5965
{
6066
get
6167
{
62-
if(!IsAuthorised)
68+
if (UserID != null)
6369
{
64-
return UserInviteStatusEnum.AuthorisationRequired;
70+
return UserInviteStatusEnum.Accepted;
6571
}
6672

67-
if (UserID != null)
73+
if (!IsAuthorised)
6874
{
69-
return UserInviteStatusEnum.Accepted;
75+
return UserInviteStatusEnum.AuthorisationRequired;
7076
}
71-
72-
if ((DateTimeOffset.Now - LastInvited) > new TimeSpan(USER_INVITE_EXPIRATION_HOURS, 0, 0))
77+
else if ((DateTimeOffset.Now - LastInvited) > new TimeSpan(USER_INVITE_EXPIRATION_HOURS, 0, 0))
7378
{
7479
return UserInviteStatusEnum.Expired;
7580
}
@@ -85,6 +90,8 @@ public UserInviteStatusEnum Status
8590
/// used to ensure a user invite's details are not modified between the time the
8691
/// authorisation is given and the time the user invite is enabled.
8792
/// </summary>
93+
/// <remarks>There is currently no need for a nonce as user invites cannot have their critical details updated
94+
/// once created.</remarks>
8895
/// <returns>A hash of the user invite's critical fields.</returns>
8996
public string GetApprovalHash()
9097
{

src/NoFrixion.MoneyMoov/MoneyMoovUrlBuilder.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,12 @@ public static string UserInvitesUrl(string moneyMoovBaseUrl)
315315

316316
public static string UserInviteUrl(string moneyMoovBaseUrl, Guid userInviteID)
317317
=> $"{moneyMoovBaseUrl}/{MoneyMoovResources.userinvites}/{userInviteID}";
318+
319+
public static string AuthoriseUserInviteUrl(string moneyMoovBaseUrl, Guid userInviteID)
320+
=> $"{moneyMoovBaseUrl}/{MoneyMoovResources.userinvites}/authorise/{userInviteID}";
321+
322+
public static string UserInviteDetailsUrl(string moneyMoovBaseUrl, Guid userInviteID)
323+
=> $"{moneyMoovBaseUrl}/{MoneyMoovResources.userinvites}/{userInviteID}/details";
318324
}
319325

320326
/// <summary>

0 commit comments

Comments
 (0)