Skip to content

Commit 4ae5b7d

Browse files
committed
feat: add state parameter support to OAuth provider sign-in
1 parent 18be768 commit 4ae5b7d

4 files changed

Lines changed: 39 additions & 0 deletions

File tree

Gotrue/Helpers.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ internal static ProviderAuthState GetUrlForProvider(string url, Constants.Provid
108108
if (attr == null)
109109
throw new Exception("Unknown provider");
110110

111+
var state = !string.IsNullOrEmpty(options.State) ? options.State : GenerateNonce();
112+
query.Add("state", state);
113+
result.State = state;
114+
111115
query.Add("provider", attr.Mapping);
112116

113117
if (!string.IsNullOrEmpty(options.Scopes))

Gotrue/ProviderAuthState.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ public class ProviderAuthState
1818
/// </summary>
1919
public string? PKCEVerifier { get; set; }
2020

21+
/// <summary>
22+
/// The state parameter included in the OAuth URL for CSRF protection (RFC 6749 §10.12).
23+
/// Validate this against the state echoed back in the OAuth callback.
24+
/// </summary>
25+
public string State { get; set; } = null!;
26+
2127
/// <summary>
2228
/// Constructor
2329
/// </summary>

Gotrue/SignInOptions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,13 @@ public class SignInOptions
2929
/// PKCE is recommended for mobile and server-side applications.
3030
/// </summary>
3131
public OAuthFlowType FlowType { get; set; } = OAuthFlowType.Implicit;
32+
33+
/// <summary>
34+
/// An optional state parameter for CSRF protection (RFC 6749 §10.12).
35+
/// If not provided, one will be generated automatically.
36+
/// Store the returned <see cref="ProviderAuthState.State"/> value and validate it
37+
/// against the state echoed back in the OAuth callback.
38+
/// </summary>
39+
public string? State { get; set; }
3240
}
3341
}

GotrueTests/PKCE/PkceContractTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,27 @@ public async Task ResetPasswordForEmailPkce_ChallengeIsHashOfVerifier()
7777
request.ReadJsonBodyField("code_challenge").Should().Be(S256(state.PKCEVerifier!), "server must receive BASE64URL(SHA256(verifier)) per RFC 7636 §4.2");
7878
}
7979

80+
[TestMethod("SignInWithProvider: auto-generates state and includes it in the URL")]
81+
public async Task SignInWithProvider_AutoGeneratesState()
82+
{
83+
var result = await client.SignIn(Provider.Github, new SignInOptions { FlowType = OAuthFlowType.PKCE });
84+
result.State.Should().NotBeNullOrEmpty();
85+
result.Uri.Query.Should().Contain($"state={result.State}");
86+
}
87+
88+
[TestMethod("SignInWithProvider: uses developer-provided state and includes it in the URL")]
89+
public async Task SignInWithProvider_UsesDeveloperProvidedState()
90+
{
91+
var customState = "my-server-generated-csrf-token";
92+
var result = await client.SignIn(Provider.Github, new SignInOptions
93+
{
94+
FlowType = OAuthFlowType.PKCE,
95+
State = customState
96+
});
97+
result.State.Should().Be(customState);
98+
result.Uri.Query.Should().Contain($"state={customState}");
99+
}
100+
80101
private static string S256(string verifier)
81102
{
82103
using var sha = SHA256.Create();

0 commit comments

Comments
 (0)