DEV Community

Cover image for Serving DocFX static site from .NET & adding Authentication
Bazen
Bazen

Posted on

Serving DocFX static site from .NET & adding Authentication

For those of you who are new to DocFX, DocFX is static site generator from source code files and markdown. it is mainly used for documentation, however it is important to mention that it is flexible to be used for many different purposes. some use it for blogging, profile site,... really its up to you to define what to use it for. for more information on DocFX you can visit their official website.

Why serve it from .NET?
There are different advantages to serving the static site generated by DocFX from .NET since it provides the ability to add different functionalities on top of the static site. one of the practical advantages is to add authentication to access the static site. the reason to adding the authentication could be if you have internal documentation with in your company and you want it to be accessible only by authorized person. this scenario is only to demonstrate practical use case, & the use case can be different depending on your scenario. with that being said, lets get started...

Pre-request

  1. IdentityServer4: For authentication
  2. DocFX: For generating static site
  3. Add DocFX to environment variable for smooth build process

I used IdentityServer4 for authentication in this article, however this implementation works fine with other methods of authentications as well.

Setting up IdentityServer4

Create IdentityServer4 project

dotnet new is4inmem -n Identity
Enter fullscreen mode Exit fullscreen mode

The templates for IdentityServer4 projects can be found on IdentityServer's Github repo.once the Identity project is created there is no need to make any change.

Setting up Static site server

Create ASP.NET Api project

dotnet new webapi -n StaticSiteServer
Enter fullscreen mode Exit fullscreen mode

Add reference to Microsoft.AspNetCore.Authentication.OpenIdConnect Nuget package.

In setting up the authentication there are couple of changes to be made here and there. not to go off topic I will not covering that in this article. however the source code can be found in my Github repository.

Setting up static site

In the same directory as StaticSiteServer create static site project

docfx init -q -o Docs
Enter fullscreen mode Exit fullscreen mode

Setting up the build process

Update StaticSiteServer.csproj to define the root directory of the DocFX file system

<PropertyGroup>
      .
      .
      .
    <DocFXRoot>Docs\</DocFXRoot>
</PropertyGroup>
Enter fullscreen mode Exit fullscreen mode

Update the StaticSiteServer.csproj to generate static site on both during build & publish.

<ItemGroup>
        <Content Remove="$(DocFXRoot)**" />
        <None Remove="$(DocFXRoot)**" />
        <None Include="$(DocFXRoot)**" Exclude="$(DocFXRoot)_site\**" />
    </ItemGroup>
    <Target Name="DebugEnsureDocFXEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(DocFXRoot)node_modules') ">
        <Exec Command="docfx --version" ContinueOnError="true">
            <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
        </Exec>
        <Error Condition="'$(ErrorCode)' != '0'" Text="DocFX is required to build and run this project. To continue, please install DocFX from https://github.com/dotnet/docfx/releases, and then restart your command prompt or IDE." />
        <Message Importance="high" Text="Generate static sites from Markdown and code files. This may take several minutes..." />
        <Exec WorkingDirectory="$(DocFXRoot)" Command="docfx build" />
    </Target>
    <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
        <Exec WorkingDirectory="$(DocFXRoot)" Command="docfx build" />
        <ItemGroup>
            <DistFiles Include="$(DocFXRoot)_site\**" />
            <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
                <RelativePath>%(DistFiles.Identity)</RelativePath>
                <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
                <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
            </ResolvedFileToPublish>
        </ItemGroup>
    </Target>   
Enter fullscreen mode Exit fullscreen mode

Now with these changes the static site will be generated build time. but the static site is still not being server from the StaticSiteServer web API. for that lets update the Startup.cs...
In ConfigureServices method add authorization

public void ConfigureServices(IServiceCollection services)
{
  .
  .
  .
  services.AddAuthorization(options =>
              {
                  options.FallbackPolicy = new 
  AuthorizationPolicyBuilder()
                      .RequireAuthenticatedUser()
                      .Build();
              });
  .
  .
  .
}
Enter fullscreen mode Exit fullscreen mode

Again in the Startup.cs file update the Configure method to serve static files.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
 .
 .
 .
  app.UseFileServer(new FileServerOptions
              {
                  FileProvider = new 
  PhysicalFileProvider(
                  Path.Combine(env.ContentRootPath, 
  "Docs", "_site")),
              });
  .
  .
  .
}
Enter fullscreen mode Exit fullscreen mode

NOTE app.UseFileServer* must be added after app.UseAuthentication() and app.UseAuthorization().

That's it. now when trying to access the StaticSiteServer it will redirects user for authentication, once the user is authenticated he will be presented with the static site as shown below.
1. StaticSiteServer
Alt StaticSiteServer
2. Authentication page
Alt Authentication page
3. Documentation static site
Alt Documentation static site1
4. Documentation static site
Alt Documentation static site2

Complete source code can be found on Github

Thanks for reading, cheers!

Top comments (6)

Collapse
 
klacol profile image
klacol • Edited

Thanks for this article. Exactly my scenario and very good description. Do you think, the StaticSiteServer could be deployed as Azure Static Web App? They require an index.html in the root. How could this be achieved with your project?

Collapse
 
bazen profile image
Bazen

I don't have a lot of experience with Azure Static Web App specifically but generally speaking: StaticSiteServer serves the static files which is also what Azure Static Web App does, so I don't see a way of using both in conjunction as both do somewhat similar task. having said that this is what I would suggest

  1. Azure static web app provide a way of adding authorization so I would recommend checking if they fit your specific use case. here are some links that might help Authenticate users with Azure Static Web Apps, Authentication and authorization for Azure Static Web Apps

  2. If the first solution doesn't fit your scenario and you want to use StaticSiteServer. one way to go about it would be to deploy StaticSiteServer as webapi so that it will handle serving the static files while adding your custom authentication.

Collapse
 
mdguest profile image
Madhava

All Good, is it possible to implement logout button, I mean once user is logged in if he wants to logout, how user will logout.

Collapse
 
bazen profile image
Bazen

Yes, there are different ways you can do that. One straight forward approach is to add logout button to your static site (in this case the documentation documentation) header that calls the logout endpoint on your backend.

Collapse
 
ogameleira profile image
Luciano Deyvis Almeida Gameleira

Hi! @bazenteklehaymanot, I did not found the license into the git [ github.com/bazen-teklehaymanot/ser... ]. Can I consider this source code as a MIT one?

Collapse
 
bazen profile image
Bazen

Hi @ogameleira, I've added license(MIT) into the sample code. feel free to use it.