TL;DR
If you have very simple scenarios, like a single client application, a single API then it might not pay off to go OAuth 2.0, on the other hand, lots of different clients (browser-based, native mobile, server-side, etc) then sticking to OAuth 2.0 rules might make it more manageable than trying to roll your own system.
As stated in another answer, JWT (Learn JSON Web Tokens) is just a token format, it defines a compact and self-contained mechanism for transmitting data between parties in a way that can be verified and trusted because it is digitally signed. Additionally, the encoding rules of a JWT also make these tokens very easy to use within the context of HTTP.
Being self-contained (the actual token contains information about a given subject) they are also a good choice for implementing stateless authentication mechanisms (aka Look mum, no sessions!). When going this route and the only thing a party must present to be granted access to a protected resource is the token itself, the token in question can be called a bearer token.
In practice, what you're doing can already be classified as based on bearer tokens. However, do consider that you're not using bearer tokens as specified by the OAuth 2.0 related specs (see RFC 6750). That would imply, relying on the Authorization
HTTP header and using the Bearer
authentication scheme.
Regarding the use of the JWT to prevent CSRF without knowing exact details it's difficult to ascertain the validity of that practice, but to be honest it does not seem correct and/or worthwhile. The following article (Cookies vs Tokens: The Definitive Guide) may be a useful read on this subject, particularly the XSS and XSRF Protection section.
One final piece of advice, even if you don't need to go full OAuth 2.0, I would strongly recommend on passing your access token within the Authorization
header instead of going with custom headers. If they are really bearer tokens, follow the rules of RFC 6750. If not, you can always create a custom authentication scheme and still use that header.
Authorization headers are recognized and specially treated by HTTP proxies and servers. Thus, the usage of such headers for sending access tokens to resource servers reduces the likelihood of leakage or unintended storage of authenticated requests in general, and especially Authorization headers.
(source: RFC 6819, section 5.4.1)
Best Solution
You can totally achieve what you want:
Let's go through the differences between your code and that one.
AddAuthentication
has no parameterIf you set a default authentication scheme, then on every single request the authentication middleware will try to run the authentication handler associated with the default authentication scheme. Since we now have two opssible authentication schemes, there's no point in running one of them.
Use another overload of
AddJwtBearer
Every single
AddXXX
method to add an authentication has several overloads:Now, because you use the same authentication method twice but authentication schemes must be unique, you need to use the second overload.
Update the default policy
Since the requests won't be authenticated automatically anymore, putting
[Authorize]
attributes on some actions will result in the requests being rejected and anHTTP 401
will be issued.Since that's not what we want because we want to give the authentication handlers a chance to authenticate the request, we change the default policy of the authorization system by indicating both the
Firebase
andCustom
authentication schemes should be tried to authenticate the request.That doesn't prevent you from being more restrictive on some actions; the
[Authorize]
attribute has anAuthenticationSchemes
property that allows you to override which authentication schemes are valid.If you have more complex scenarios, you can make use of policy-based authorization. I find the official documentation is great.
Let's imagine some actions are only available to JWT tokens issued by Firebase and must have a claim with a specific value; you could do it this way:
You could then use
[Authorize(Policy = "FirebaseAdministrators")]
on some actions.A final point to note: If you are catching
AuthenticationFailed
events and using anything but the firstAddJwtBearer
policy, you may seeIDX10501: Signature validation failed. Unable to match key...
This is caused by the system checking eachAddJwtBearer
in turn until it gets a match. The error can usually be ignored.