.github
.idx
DysonNetwork.Sphere
Account
Activity
Auth
OpenId
AppleMobileSignInRequest.cs
AppleOidcService.cs
ConnectionController.cs
DiscordOidcService.cs
GitHubOidcService.cs
GoogleOidcService.cs
MicrosoftOidcService.cs
OidcController.cs
OidcService.cs
OidcState.cs
OidcUserInfo.cs
Auth.cs
AuthController.cs
AuthService.cs
CheckpointModel.cs
CompactTokenService.cs
Session.cs
Chat
Connection
Developer
Email
Localization
Migrations
Pages
Permission
Post
Properties
Publisher
Realm
Resources
Sticker
Storage
Wallet
wwwroot
.DS_Store
.gitignore
AppDatabase.cs
Dockerfile
DysonNetwork.Sphere.csproj
DysonNetwork.Sphere.csproj.DotSettings.user
DysonNetwork.Sphere.http
Program.cs
README.md
appsettings.json
package.json
postcss.config.js
tailwind.config.js
.dockerignore
.gitignore
DysonNetwork.sln
DysonNetwork.sln.DotSettings.user
LICENSE.txt
README.md
compose.yaml
124 lines
5.0 KiB
C#
124 lines
5.0 KiB
C#
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using DysonNetwork.Sphere.Storage;
|
|
|
|
namespace DysonNetwork.Sphere.Auth.OpenId;
|
|
|
|
public class MicrosoftOidcService(
|
|
IConfiguration configuration,
|
|
IHttpClientFactory httpClientFactory,
|
|
AppDatabase db,
|
|
AuthService auth,
|
|
ICacheService cache
|
|
)
|
|
: OidcService(configuration, httpClientFactory, db, auth, cache)
|
|
{
|
|
public override string ProviderName => "Microsoft";
|
|
|
|
protected override string DiscoveryEndpoint => Configuration[$"Oidc:{ConfigSectionName}:DiscoveryEndpoint"] ??
|
|
throw new InvalidOperationException(
|
|
"Microsoft OIDC discovery endpoint is not configured.");
|
|
|
|
protected override string ConfigSectionName => "Microsoft";
|
|
|
|
public override string GetAuthorizationUrl(string state, string nonce)
|
|
{
|
|
var config = GetProviderConfig();
|
|
var discoveryDocument = GetDiscoveryDocumentAsync().GetAwaiter().GetResult();
|
|
if (discoveryDocument?.AuthorizationEndpoint == null)
|
|
throw new InvalidOperationException("Authorization endpoint not found in discovery document.");
|
|
|
|
var queryParams = new Dictionary<string, string>
|
|
{
|
|
{ "client_id", config.ClientId },
|
|
{ "response_type", "code" },
|
|
{ "redirect_uri", config.RedirectUri },
|
|
{ "response_mode", "query" },
|
|
{ "scope", "openid profile email" },
|
|
{ "state", state },
|
|
{ "nonce", nonce },
|
|
};
|
|
|
|
var queryString = string.Join("&", queryParams.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}"));
|
|
return $"{discoveryDocument.AuthorizationEndpoint}?{queryString}";
|
|
}
|
|
|
|
public override async Task<OidcUserInfo> ProcessCallbackAsync(OidcCallbackData callbackData)
|
|
{
|
|
var tokenResponse = await ExchangeCodeForTokensAsync(callbackData.Code);
|
|
if (tokenResponse?.AccessToken == null)
|
|
{
|
|
throw new InvalidOperationException("Failed to obtain access token from Microsoft");
|
|
}
|
|
|
|
var userInfo = await GetUserInfoAsync(tokenResponse.AccessToken);
|
|
|
|
userInfo.AccessToken = tokenResponse.AccessToken;
|
|
userInfo.RefreshToken = tokenResponse.RefreshToken;
|
|
|
|
return userInfo;
|
|
}
|
|
|
|
protected override async Task<OidcTokenResponse?> ExchangeCodeForTokensAsync(string code,
|
|
string? codeVerifier = null)
|
|
{
|
|
var config = GetProviderConfig();
|
|
var discoveryDocument = await GetDiscoveryDocumentAsync();
|
|
if (discoveryDocument?.TokenEndpoint == null)
|
|
{
|
|
throw new InvalidOperationException("Token endpoint not found in discovery document.");
|
|
}
|
|
|
|
var client = HttpClientFactory.CreateClient();
|
|
|
|
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, discoveryDocument.TokenEndpoint)
|
|
{
|
|
Content = new FormUrlEncodedContent(new Dictionary<string, string>
|
|
{
|
|
{ "client_id", config.ClientId },
|
|
{ "scope", "openid profile email" },
|
|
{ "code", code },
|
|
{ "redirect_uri", config.RedirectUri },
|
|
{ "grant_type", "authorization_code" },
|
|
{ "client_secret", config.ClientSecret },
|
|
})
|
|
};
|
|
|
|
var response = await client.SendAsync(tokenRequest);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
return await response.Content.ReadFromJsonAsync<OidcTokenResponse>();
|
|
}
|
|
|
|
private async Task<OidcUserInfo> GetUserInfoAsync(string accessToken)
|
|
{
|
|
var discoveryDocument = await GetDiscoveryDocumentAsync();
|
|
if (discoveryDocument?.UserinfoEndpoint == null)
|
|
throw new InvalidOperationException("Userinfo endpoint not found in discovery document.");
|
|
|
|
var client = HttpClientFactory.CreateClient();
|
|
var request = new HttpRequestMessage(HttpMethod.Get, discoveryDocument.UserinfoEndpoint);
|
|
request.Headers.Add("Authorization", $"Bearer {accessToken}");
|
|
|
|
var response = await client.SendAsync(request);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var microsoftUser = JsonDocument.Parse(json).RootElement;
|
|
|
|
return new OidcUserInfo
|
|
{
|
|
UserId = microsoftUser.GetProperty("sub").GetString() ?? "",
|
|
Email = microsoftUser.TryGetProperty("email", out var emailElement) ? emailElement.GetString() : null,
|
|
DisplayName =
|
|
microsoftUser.TryGetProperty("name", out var nameElement) ? nameElement.GetString() ?? "" : "",
|
|
PreferredUsername = microsoftUser.TryGetProperty("preferred_username", out var preferredUsernameElement)
|
|
? preferredUsernameElement.GetString() ?? ""
|
|
: "",
|
|
ProfilePictureUrl = microsoftUser.TryGetProperty("picture", out var pictureElement)
|
|
? pictureElement.GetString() ?? ""
|
|
: "",
|
|
Provider = ProviderName
|
|
};
|
|
}
|
|
} |