Serving DocFX static site from .NET & adding Authentication
Tags: dotnet, docfx, staticsite, auth
Published: 2021-06-16 12:58:56

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](https://dotnet.github.io/docfx/). 

**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](https://identityserver4.readthedocs.io/en/latest/): For authentication
1. [DocFX](https://dotnet.github.io/docfx/): For generating static site
1. 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
```
The templates for `IdentityServer4` projects can be found on [IdentityServer's Github repo](https://github.com/IdentityServer/IdentityServer4.Templates).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
```
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](https://github.com/bazen-teklehaymanot/serve-docfx-net). 

### Setting up static site
In the same directory as `StaticSiteServer` create static site project
```
docfx init -q -o Docs
```
### Setting up the build process
Update `StaticSiteServer.csproj` to define the root directory of the `DocFX` file system
```
<PropertyGroup>
      .
      .
      .
    <DocFXRoot>Docs\</DocFXRoot>
</PropertyGroup>
``` 
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>	
```
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();
              });
  .
  .
  .
}
```
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")),
              });
  .
  .
  .
}
```
> **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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/862qeffhvi2x4thn3jk2.png)
**2. Authentication page**
![Alt Authentication page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xx8k7m41idzwzz961yvg.png)
**3. Documentation static site** 
![Alt Documentation static site1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mt2skszcovhs9xc02cyc.png)
**4. Documentation static site**
![Alt Documentation static site2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fmr61bs3s7wavr4k1f5.png)


Complete source code can be found on [Github](https://github.com/bazen-teklehaymanot/serve-docfx-net)

Thanks for reading, cheers!