How to add bearer token authentication to NSwag api.

NSwag is a game changer for working with Angular and Net Core, you write your endpoint code, and swashbuckler generates your swagger docs. You can find more info on swashbuckler here:

https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-6.0&tabs=visual-studio

Where NSwag helps, is that it looks at the swagger document and generates all the typescript code, which includes the models, the functions that fetch the data from the controllers, so that you can get started faster with building the app. You can check it out here:

https://github.com/RicoSuter/NSwag/releases

This is all well and good, but what about stopping unauthorised users accessing your data? Lets look at an example.

Lets say you write a controller that gets a list of things. But your controller should only hand out this info to authorised users. So we set up out authentication middleware and put an attribute above our end point like so.

// GET: api/Product
[Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme)]
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Product>>> GetProduct()
        {
            return await _context.Products.ToListAsync();
        }

How do we configure nswag to pass credentials?

To start with we need something to check if the user is authenticated, and if they are not, we need to authenticate them. For this I have written this:

 handleLoginObservable():Observable<User> {
    return new Observable(subscriber => {
      //is the user logged in?
      if (this.isLoggedIn) {
        //the user is logged in
        //is the users token valid?
        if (this.oidcHelperService.isSessionExpired) {
          //refresh the token
          this.refreshLogin().pipe(take(1)).subscribe(r => { subscriber.next(this.currentUser) })
        }
        else {
          subscriber.next(this.currentUser)
        }
      }
      else {
        this.loginStatus.subscribe(i => {
          if (i == true) {
            subscriber.next(this.currentUser)
          }
        })
        this.reLogin()
      }
    })
  }

Then we can use this in our base class to preceed all of our API call so that we are always checking if the user is authenticated.

In order to do this we need to make this method accessible from our nswag api. NSwag has these two fields that are lifesavers – Base Class Name and Configuration Class Name:

By using these two fields, we are telling NSwag that we are going to have a base class called AuthorizedApiBase and a config class called IConfig.

In addition we need to tick Call ‘transformOptions’ on the base class or extension class

I have included the two classes below:

export class AuthorizedApiBase {
  private readonly config: IConfig;

  protected constructor(config: IConfig,) {
    //pull in the IConfig class so
    //so we can access the extra methods that are contained in EndpointBase
    this.config = config;
  }

  protected async transformOptions(options: any): Promise<any> {

    return new Promise((resolve, reject) => {

      this.config.authService.handleLoginObservable().subscribe(i => {
        //we want to start this to check the login 'state'
        //of the user. Once this is returned, 
        //it means we have authenticated and should have
        //the required bearer and header info

        let bearer = this.config.getBearer
        let bearertest = this.config.requestHeaders.headers
        //console.log('adding', bearer)
        options.headers = options.headers.append(
          'authorization',
          `Bearer ${bearer}`,
        )
        //we can now resolve the options.headers with the auth info 😎
        resolve(options)
      })
    })
  }
}

Becuase transformOptions is called on all API endpoints, we are now including some logic to make sure that all requests are handled by checking if the user, is logged in, if their bearer token is current and if they are not, we are presenting a login screen to them.

What about if we need to expose some endpoints?

In most projects, not all the endpoints are locked down, so in this instance we can create another NSwag config to generate our Angular API but this time we just use the default settings. So we end up with an authorised angular api and a non authorised one👍


Posted

in

, , , ,

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *