Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public void CreateProviderConfig()
Assert.True(config.Enabled);
Assert.Equal("OIDC_CLIENT_ID", config.ClientId);
Assert.Equal("https://oidc.com/issuer", config.Issuer);
Assert.Equal("OIDC_CLIENT_SECRET", config.ClientSecret);
Assert.True(config.CodeResponseType);
Assert.False(config.IdTokenResponseType);
}

[Fact]
Expand All @@ -59,6 +62,9 @@ public async Task GetProviderConfig()
Assert.True(config.Enabled);
Assert.Equal("OIDC_CLIENT_ID", config.ClientId);
Assert.Equal("https://oidc.com/issuer", config.Issuer);
Assert.Equal("OIDC_CLIENT_SECRET", config.ClientSecret);
Assert.True(config.CodeResponseType);
Assert.False(config.IdTokenResponseType);
}

[Fact]
Expand All @@ -84,6 +90,9 @@ public async Task ListProviderConfig()
Assert.True(config.Enabled);
Assert.Equal("OIDC_CLIENT_ID", config.ClientId);
Assert.Equal("https://oidc.com/issuer", config.Issuer);
Assert.Equal("OIDC_CLIENT_SECRET", config.ClientSecret);
Assert.True(config.CodeResponseType);
Assert.False(config.IdTokenResponseType);
}

[Fact]
Expand All @@ -97,6 +106,9 @@ public async Task UpdateProviderConfig()
Enabled = false,
ClientId = "UPDATED_OIDC_CLIENT_ID",
Issuer = "https://oidc.com/updated-issuer",
ClientSecret = "UPDATED_OIDC_CLIENT_SECRET",
CodeResponseType = false,
IdTokenResponseType = true,
};

var config = await this.auth.UpdateProviderConfigAsync(args);
Expand All @@ -106,6 +118,9 @@ public async Task UpdateProviderConfig()
Assert.False(config.Enabled);
Assert.Equal("UPDATED_OIDC_CLIENT_ID", config.ClientId);
Assert.Equal("https://oidc.com/updated-issuer", config.Issuer);
Assert.Equal("UPDATED_OIDC_CLIENT_SECRET", config.ClientSecret);
Assert.False(config.CodeResponseType);
Assert.True(config.IdTokenResponseType);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public OidcProviderConfigFixture()
Enabled = true,
ClientId = "OIDC_CLIENT_ID",
Issuer = "https://oidc.com/issuer",
ClientSecret = "OIDC_CLIENT_SECRET",
CodeResponseType = true,
IdTokenResponseType = false,
};
this.ProviderConfig = this.Auth.CreateProviderConfigAsync(args).Result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ public class OidcProviderConfigTest
""clientId"": ""CLIENT_ID"",
""issuer"": ""https://oidc.com/issuer"",
""displayName"": ""oidcProviderName"",
""enabled"": true
""enabled"": true,
""clientSecret"": ""CLIENT_SECRET"",
""responseType"": {
""code"": true,
""idToken"": true
}
}";

private static readonly IList<string> ListConfigsResponses = new List<string>()
Expand Down Expand Up @@ -135,6 +140,9 @@ public async Task CreateConfig(ProviderTestConfig config)
Enabled = true,
ClientId = "CLIENT_ID",
Issuer = "https://oidc.com/issuer",
ClientSecret = "CLIENT_SECRET",
CodeResponseType = true,
IdTokenResponseType = true,
};

var provider = await auth.CreateProviderConfigAsync(args);
Expand All @@ -147,11 +155,14 @@ public async Task CreateConfig(ProviderTestConfig config)

var body = NewtonsoftJsonSerializer.Instance.Deserialize<JObject>(
handler.LastRequestBody);
Assert.Equal(4, body.Count);
Assert.Equal(6, body.Count);
Assert.Equal("oidcProviderName", body["displayName"]);
Assert.True((bool)body["enabled"]);
Assert.Equal("CLIENT_ID", body["clientId"]);
Assert.Equal("https://oidc.com/issuer", body["issuer"]);
Assert.Equal("CLIENT_SECRET", body["clientSecret"]);
Assert.True((bool)body["responseType"]["code"]);
Assert.True((bool)body["responseType"]["idToken"]);
}

[Theory]
Expand Down Expand Up @@ -252,6 +263,9 @@ public async Task UpdateConfig(ProviderTestConfig config)
Enabled = true,
ClientId = "CLIENT_ID",
Issuer = "https://oidc.com/issuer",
ClientSecret = "CLIENT_SECRET",
CodeResponseType = true,
IdTokenResponseType = true,
};

var provider = await auth.UpdateProviderConfigAsync(args);
Expand All @@ -260,16 +274,19 @@ public async Task UpdateConfig(ProviderTestConfig config)
Assert.Equal(1, handler.Requests.Count);
var request = handler.Requests[0];
Assert.Equal(ProviderTestConfig.PatchMethod, request.Method);
var mask = "clientId,displayName,enabled,issuer";
var mask = "clientId,clientSecret,displayName,enabled,issuer,responseType.code,responseType.idToken";
config.AssertRequest($"oauthIdpConfigs/oidc.provider?updateMask={mask}", request);

var body = NewtonsoftJsonSerializer.Instance.Deserialize<JObject>(
handler.LastRequestBody);
Assert.Equal(4, body.Count);
Assert.Equal(6, body.Count);
Assert.Equal("oidcProviderName", body["displayName"]);
Assert.True((bool)body["enabled"]);
Assert.Equal("CLIENT_ID", body["clientId"]);
Assert.Equal("https://oidc.com/issuer", body["issuer"]);
Assert.Equal("CLIENT_SECRET", body["clientSecret"]);
Assert.True((bool)body["responseType"]["code"]);
Assert.True((bool)body["responseType"]["idToken"]);
}

[Theory]
Expand Down Expand Up @@ -621,6 +638,9 @@ private void AssertOidcProviderConfig(OidcProviderConfig provider)
Assert.True(provider.Enabled);
Assert.Equal("CLIENT_ID", provider.ClientId);
Assert.Equal("https://oidc.com/issuer", provider.Issuer);
Assert.Equal("CLIENT_SECRET", provider.ClientSecret);
Assert.True(provider.CodeResponseType);
Assert.True(provider.IdTokenResponseType);
}

public class InvalidCreateArgs : IEnumerable<object[]>
Expand Down Expand Up @@ -682,6 +702,29 @@ public IEnumerator<object[]> MakeEnumerator()
},
"Malformed issuer string: not a url",
};
yield return new object[]
{
new OidcProviderConfigArgs()
{
ProviderId = "oidc.provider",
ClientId = "CLIENT_ID",
Issuer = "https://oidc.com/issuer",
CodeResponseType = true,
},
"Client Secret must not be null or empty for code response type",
};
yield return new object[]
{
new OidcProviderConfigArgs()
{
ProviderId = "oidc.provider",
ClientId = "CLIENT_ID",
Issuer = "https://oidc.com/issuer",
CodeResponseType = false,
IdTokenResponseType = false,
},
"At least one response type must be returned.",
};
}

IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
Expand Down Expand Up @@ -754,6 +797,27 @@ public IEnumerator<object[]> MakeEnumerator()
},
"Malformed issuer string: not a url",
};
yield return new object[]
{
new OidcProviderConfigArgs()
{
ProviderId = "oidc.provider",
Issuer = "https://oidc.com/issuer",
CodeResponseType = true,
},
"Client Secret must not be null or empty for code response type",
};
yield return new object[]
{
new OidcProviderConfigArgs()
{
ProviderId = "oidc.provider",
Issuer = "https://oidc.com/issuer",
CodeResponseType = false,
IdTokenResponseType = false,
},
"At least one response type must be returned.",
};
}

IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
Expand Down
33 changes: 33 additions & 0 deletions FirebaseAdmin/FirebaseAdmin/Auth/Providers/OidcProviderConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ internal OidcProviderConfig(Request request)
{
this.ClientId = request.ClientId;
this.Issuer = request.Issuer;
this.ClientSecret = request.ClientSecret;
this.IdTokenResponseType = request.ResponseType.IdToken == true;
this.CodeResponseType = request.ResponseType.Code == true;
}

/// <summary>
Expand Down Expand Up @@ -63,13 +66,43 @@ internal OidcProviderConfig(Request request)
/// </summary>
public string Issuer { get; }

/// <summary>
/// Gets the Client Secret used to verify code based response types.
/// </summary>
public string ClientSecret { get; }

/// <summary>
/// Gets a value indicating whether an ID Token response type will be provided.
/// </summary>
public bool IdTokenResponseType { get; }

/// <summary>
/// Gets a value indicating whether an Code type response type will be provided.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Gets a value indicating whether an Code type response type will be provided.
/// Gets a value indicating whether a Code type response type will be provided.

/// </summary>
public bool CodeResponseType { get; }

internal sealed class ResponseTypeInfo
{
[JsonProperty("code")]
internal bool? Code { get; set; }

[JsonProperty("idToken")]
internal bool? IdToken { get; set; }
}

internal sealed new class Request : AuthProviderConfig.Request
{
[JsonProperty("clientId")]
internal string ClientId { get; set; }

[JsonProperty("issuer")]
internal string Issuer { get; set; }

[JsonProperty("clientSecret")]
internal string ClientSecret { get; set; }

[JsonProperty("responseType")]
internal ResponseTypeInfo ResponseType { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ public sealed class OidcProviderConfigArgs : AuthProviderConfigArgs<OidcProvider
/// </summary>
public string Issuer { get; set; }

/// <summary>
/// Gets or sets the Client Secret used to verify code based response types.
/// </summary>
public string ClientSecret { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this OIDC provider uses an ID-token based response type.
/// </summary>
public bool? IdTokenResponseType { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this OIDC provider uses a code based response type.
/// </summary>
public bool? CodeResponseType { get; set; }

internal override AuthProviderConfig.Request ToCreateRequest()
{
var req = new OidcProviderConfig.Request()
Expand All @@ -64,7 +79,17 @@ internal override AuthProviderConfig.Request ToCreateRequest()
Enabled = this.Enabled,
ClientId = this.ClientId,
Issuer = this.Issuer,
ClientSecret = this.ClientSecret,
};
if (this.CodeResponseType != null || this.IdTokenResponseType != null)
{
req.ResponseType = new OidcProviderConfig.ResponseTypeInfo()
{
Code = this.CodeResponseType,
IdToken = this.IdTokenResponseType,
};
}

if (string.IsNullOrEmpty(req.ClientId))
{
throw new ArgumentException("Client ID must not be null or empty.");
Expand All @@ -79,6 +104,16 @@ internal override AuthProviderConfig.Request ToCreateRequest()
throw new ArgumentException($"Malformed issuer string: {req.Issuer}");
}

if (req.ResponseType?.Code == true && string.IsNullOrEmpty(req.ClientSecret))
{
throw new ArgumentException("Client Secret must not be null or empty for code response type");
}

if (req.ResponseType?.Code == false && req.ResponseType?.IdToken == false)
{
throw new ArgumentException("At least one response type must be returned.");
}

return req;
}

Expand All @@ -90,7 +125,16 @@ internal override AuthProviderConfig.Request ToUpdateRequest()
Enabled = this.Enabled,
ClientId = this.ClientId,
Issuer = this.Issuer,
ClientSecret = this.ClientSecret,
};
if (this.CodeResponseType != null || this.IdTokenResponseType != null)
{
req.ResponseType = new OidcProviderConfig.ResponseTypeInfo()
{
Code = this.CodeResponseType,
IdToken = this.IdTokenResponseType,
};
}

if (req.ClientId == string.Empty)
{
Expand All @@ -106,6 +150,16 @@ internal override AuthProviderConfig.Request ToUpdateRequest()
throw new ArgumentException($"Malformed issuer string: {req.Issuer}");
}

if (req.ResponseType?.Code == true && string.IsNullOrEmpty(req.ClientSecret))
{
throw new ArgumentException("Client Secret must not be null or empty for code response type");
}

if (req.ResponseType?.Code == false && req.ResponseType?.IdToken == false)
{
throw new ArgumentException("At least one response type must be returned.");
}

return req;
}

Expand Down