Skip to content

Commit

Permalink
Switch to using new identity server auth flow (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwasplund authored Jul 19, 2023
1 parent dc6132b commit 8233c5c
Show file tree
Hide file tree
Showing 14 changed files with 244 additions and 126 deletions.
2 changes: 1 addition & 1 deletion Docs/Samples/C/Build-Extension.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# C++ Build Extension
# C Build Extension
This is a console application that has a custom build extension that alters the build state. The custom build Tasks will run before and after the core Build Task and will simply print a nice hello message.

[Source](https://github.com/SoupBuild/Soup/tree/main/Samples/C/BuildExtension)
Expand Down
2 changes: 1 addition & 1 deletion Docs/Samples/C/Console-Application.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# C++ Console Application
# C Console Application
This is the smallest amount of code to get a console application building using Soup.

[Source](https://github.com/SoupBuild/Soup/tree/main/Samples/C/ConsoleApplication)
Expand Down
2 changes: 1 addition & 1 deletion Docs/Samples/C/Dynamic-Library.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# C++ Dynamic Library
# C Dynamic Library
This is a console application that has a single dynamic library dependency.

[Source](https://github.com/SoupBuild/Soup/tree/main/Samples/C/DynamicLibrary)
Expand Down
2 changes: 1 addition & 1 deletion Docs/Samples/C/Static-Library.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Static Library Reference
# C Static Library Reference
This is a console application that has a single static library dependency.

[Source](https://github.com/SoupBuild/Soup/tree/main/Samples/C/StaticLibrary)
Expand Down
2 changes: 1 addition & 1 deletion Docs/Samples/C/Windows-Application.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# C++ Windows Application
# C Windows Application
This is a windows application that demonstrates creating a GUI windows application.

[Source](https://github.com/SoupBuild/Soup/tree/main/Samples/C/WindowsApplication)
Expand Down
2 changes: 1 addition & 1 deletion Samples/Cpp/DirectX/PackageLock.sml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Version: 4
Closures: {
Root: {
"C++": [
{ Name: "Samples.Cpp.DirectX", Version: "./", Build: "Build0", Tool: "Tool0" }
{ Name: "Samples.Cpp.DirectX", Version: "../DirectX", Build: "Build0", Tool: "Tool0" }
]
}
Build0: {
Expand Down
4 changes: 2 additions & 2 deletions Scripts/Windows/build-packagemanager.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ SET SourceDir=%RootDir%\Source
SET PackageManagerDir=%SourceDir%\GenerateSharp\PackageManager

REM - Build PackageManager
echo dotnet publish %PackageManagerDir% -c %Flavor% -f net7.0-windows10.0.17763.0 -r win-x64 --self-contained
call dotnet publish %PackageManagerDir% -c %Flavor% -f net7.0-windows10.0.17763.0 -r win-x64 --self-contained
echo dotnet publish %PackageManagerDir% -c %Flavor% -f net7.0 -r win-x64 --self-contained
call dotnet publish %PackageManagerDir% -c %Flavor% -f net7.0 -r win-x64 --self-contained
if %ERRORLEVEL% NEQ 0 exit /B %ERRORLEVEL%
2 changes: 1 addition & 1 deletion Scripts/Windows/soup.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ robocopy %GlobalOutDir%\Wren\Soup.CSharp\%SOUP_CSHARP_VERSION%\%ConfigHash%\ %Ru
robocopy %GlobalPackagesDir%\Wren\Soup.Wren\%SOUP_WREN_VERSION%\ %RunDir%\Soup\BuiltIn\Soup.Wren\%SOUP_WREN_VERSION%\ Recipe.sml /NJH /NJS /NDL > NUL
robocopy %GlobalOutDir%\Wren\Soup.Wren\%SOUP_WREN_VERSION%\%ConfigHash%\ %RunDir%\Soup\BuiltIn\Soup.Wren\%SOUP_WREN_VERSION%\out\ /MIR /NJH /NJS /NDL > NUL

robocopy %OutDir%\msbuild\bin\Soup.Build.PackageManager\Release\net7.0-windows10.0.17763.0\win-x64\publish\ %RunDir%\Soup\PackageManager\ /MIR /NJH /NJS /NDL > NUL
robocopy %OutDir%\msbuild\bin\Soup.Build.PackageManager\Release\net7.0\win-x64\publish\ %RunDir%\Soup\PackageManager\ /MIR /NJH /NJS /NDL > NUL

robocopy %OutDir%\msbuild\bin\Swhere\Release\net7.0\win-x64\publish\ %RunDir%\ swhere.exe /NJH /NJS /NDL > NUL

Expand Down
2 changes: 1 addition & 1 deletion Scripts/Windows/soupd.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ robocopy %GlobalOutDir%\Wren\Soup.CSharp\%SOUP_CSHARP_VERSION%\%ConfigHash%\ %Ru
robocopy %GlobalPackagesDir%\Wren\Soup.Wren\%SOUP_WREN_VERSION%\ %RunDir%\Soup\BuiltIn\Soup.Wren\%SOUP_WREN_VERSION%\ Recipe.sml /NJH /NJS /NDL > NUL
robocopy %GlobalOutDir%\Wren\Soup.Wren\%SOUP_WREN_VERSION%\%ConfigHash%\ %RunDir%\Soup\BuiltIn\Soup.Wren\%SOUP_WREN_VERSION%\out\ /MIR /NJH /NJS /NDL > NUL

robocopy %OutDir%\msbuild\bin\Soup.Build.PackageManager\Debug\net7.0-windows10.0.17763.0\win-x64\publish\ %RunDir%\Soup\PackageManager\ /MIR /NJH /NJS /NDL > NUL
robocopy %OutDir%\msbuild\bin\Soup.Build.PackageManager\Debug\net7.0\win-x64\publish\ %RunDir%\Soup\PackageManager\ /MIR /NJH /NJS /NDL > NUL
robocopy %OutDir%\msbuild\bin\Swhere\Debug\net7.0\win-x64\publish\ %RunDir%\ swhere.exe /NJH /NJS /NDL > NUL

%RunDir%\Soup.cmd %*
69 changes: 24 additions & 45 deletions Source/GenerateSharp/PackageManager/AuthenticationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,44 @@

namespace Soup.Build.PackageManager
{
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Opal;
using IdentityModel.OidcClient;

internal class AuthenticationManager : IAuthenticationManager
{
private static readonly string Tenant = $"soupbuild.com";
private static readonly string AzureAdB2CHostname = $"soupbuild.b2clogin.com";
private static readonly string ClientId = "29b9e45c-332b-4f93-a41f-af525dee4730";
private static readonly string SignUpSignInPolicyId = "B2C_1_SignUp_SignIn";
private static readonly string SoupApiScope = "/ba178231-c318-435d-881a-25f9e00df20a/soup_build_api";

private static readonly string Scope = $"https://{Tenant}{SoupApiScope}";

private static string RedirectUri => $"https://{AzureAdB2CHostname}/oauth2/nativeclient";

private static string AuthorityBase => $"https://{AzureAdB2CHostname}/tfp/{Tenant}/";

public static string SignUpSignInAuthority => $"{AuthorityBase}{SignUpSignInPolicyId}";
static string _authority = "https://auth.soupbuild.com/";
// static string _authority = "https://localhost:5001/";

/// <summary>
/// Ensure the user is logged in
/// </summary>
/// <returns>The access token</returns>
public async Task<string> EnsureSignInAsync()
{
IPublicClientApplication publicClientApp = PublicClientApplicationBuilder.Create(ClientId)
.WithB2CAuthority(SignUpSignInAuthority)
.WithRedirectUri(RedirectUri)
.WithLogging(PublicClientLog, LogLevel.Info, false)
.Build();

TokenCache.Bind(publicClientApp.UserTokenCache);

AuthenticationResult authResult;
var scopes = new string[] { Scope };

try
{
// Attempt to silently acquire the user token
var accounts = await publicClientApp.GetAccountsAsync(SignUpSignInPolicyId);
authResult = await publicClientApp.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
catch (MsalUiRequiredException)
{
// Ignore, user will need to sign in interactively.
authResult = await publicClientApp.AcquireTokenInteractive(scopes)
.ExecuteAsync();
}

return authResult.AccessToken;
var token = await Login();
return token;
}

private static void PublicClientLog(LogLevel level, string message, bool containsPii)
private async Task<string> Login()
{
string logs = $"{level} {message}";
Log.Diag(logs);
// Create a redirect URI using an available port on the loopback address
var browser = new SystemBrowser();
string redirectUri = string.Format($"http://127.0.0.1:{browser.Port}");

var options = new OidcClientOptions()
{
Authority = _authority,
ClientId = "Soup.Native",
RedirectUri = redirectUri,
Scope = "openid profile soup_api",
FilterClaims = false,
Browser = browser,
};

var oidcClient = new OidcClient(options);
var result = await oidcClient.LoginAsync(new LoginRequest());

return result.AccessToken;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"profiles": {
"Soup.Build.PackageManager": {
"commandName": "Project",
"commandLineArgs": "restore-packages [TEST_DIR]",
"commandLineArgs2": "publish-package [TEST_DIR]"
"commandLineArgs2": "restore-packages [TEST_DIR]",
"commandLineArgs": "publish-package C:/Users/mwasp/Dev/Repos/Soup/Source/GenerateSharp/Opal"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0;net7.0-windows10.0.17763.0</TargetFrameworks>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.54.1" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="7.0.1" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="IdentityModel" Version="6.1.0" />
<PackageReference Include="IdentityModel.OidcClient" Version="5.2.1" />
</ItemGroup>
<ItemGroup>
<TrimmableAssembly Include="Microsoft.Identity.Client" />
<ProjectReference Include="..\PackageManager.Core\Soup.Build.PackageManager.Core.csproj" />
</ItemGroup>
</Project>
204 changes: 204 additions & 0 deletions Source/GenerateSharp/PackageManager/SystemBrowser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// <copyright file="SystemBrowser.cs" company="Soup">
// Copyright (c) Soup. All rights reserved.
// </copyright

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using IdentityModel.OidcClient.Browser;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

namespace Soup.Build.PackageManager
{
public class SystemBrowser : IBrowser
{
public int Port { get; }
private readonly string? _path;

public SystemBrowser(int? port = null, string? path = null)
{
_path = path;

if (!port.HasValue)
{
Port = GetRandomUnusedPort();
}
else
{
Port = port.Value;
}
}

private int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}

public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default(CancellationToken))
{
using (var listener = new LoopbackHttpListener(Port, _path))
{
OpenBrowser(options.StartUrl);

try
{
var result = await listener.WaitForCallbackAsync();
if (String.IsNullOrWhiteSpace(result))
{
return new BrowserResult { ResultType = BrowserResultType.UnknownError, Error = "Empty response." };
}

return new BrowserResult { Response = result, ResultType = BrowserResultType.Success };
}
catch (TaskCanceledException ex)
{
return new BrowserResult { ResultType = BrowserResultType.Timeout, Error = ex.Message };
}
catch (Exception ex)
{
return new BrowserResult { ResultType = BrowserResultType.UnknownError, Error = ex.Message };
}
}
}

public static void OpenBrowser(string url)
{
try
{
Process.Start(url);
}
catch
{
// hack because of this: https://github.com/dotnet/corefx/issues/10361
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
url = url.Replace("&", "^&");
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true });
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Process.Start("xdg-open", url);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
Process.Start("open", url);
}
else
{
throw;
}
}
}
}

public class LoopbackHttpListener : IDisposable
{
const int DefaultTimeout = 60 * 5; // 5 mins (in seconds)

IWebHost _host;
TaskCompletionSource<string> _source = new TaskCompletionSource<string>();
string _url;

public string Url => _url;

public LoopbackHttpListener(int port, string? path = null)
{
path = path ?? String.Empty;
if (path.StartsWith("/")) path = path.Substring(1);

_url = $"http://127.0.0.1:{port}/{path}";

_host = new WebHostBuilder()
.UseKestrel()
.UseUrls(_url)
.Configure(Configure)
.Build();
_host.Start();
}

public void Dispose()
{
Task.Run(async () =>
{
await Task.Delay(500);
_host.Dispose();
});
}

void Configure(IApplicationBuilder app)
{
app.Run(async ctx =>
{
if (ctx.Request.Method == "GET")
{
await SetResultAsync(ctx.Request.QueryString.Value ?? string.Empty, ctx);
}
else if (ctx.Request.Method == "POST")
{
if (ctx.Request.ContentType is not null &&
!ctx.Request.ContentType.Equals("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
ctx.Response.StatusCode = 415;
}
else
{
using (var sr = new StreamReader(ctx.Request.Body, Encoding.UTF8))
{
var body = await sr.ReadToEndAsync();
await SetResultAsync(body, ctx);
}
}
}
else
{
ctx.Response.StatusCode = 405;
}
});
}

private async Task SetResultAsync(string value, HttpContext ctx)
{
try
{
ctx.Response.StatusCode = 200;
ctx.Response.ContentType = "text/html";
await ctx.Response.WriteAsync("<h1>You can now return to the application.</h1>");
await ctx.Response.Body.FlushAsync();

_source.TrySetResult(value);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());

ctx.Response.StatusCode = 400;
ctx.Response.ContentType = "text/html";
await ctx.Response.WriteAsync("<h1>Invalid request.</h1>");
await ctx.Response.Body.FlushAsync();
}
}

public Task<string> WaitForCallbackAsync(int timeoutInSeconds = DefaultTimeout)
{
Task.Run(async () =>
{
await Task.Delay(timeoutInSeconds * 1000);
_source.TrySetCanceled();
});

return _source.Task;
}
}
}
Loading

0 comments on commit 8233c5c

Please sign in to comment.