Can "context" from HTTPS callable Cloud Functions be trusted? - firebase-authentication

I am using Cloud Functions to handle read/write to Cloud Firestore on the server side. The Cloud Functions are triggered by clients in the web app using HTTPS callable function.
When calling a Cloud Functions using HTTPS, there is a parameter sent from the client call "context" that carries user auth information. For example, a Cloud Functions on the server can look like this:
// Saves a message to the Firebase Realtime Database but sanitizes the text by removing swearwords.
exports.addMessage = functions.https.onCall((data, context) => {
// ...
});
However, since context is passed by the client, and the client could pass in a manipulated ID token, do I need to always perform a ID token verification before trusting and using something like context.auth.uid to interact with my database?
The ID token verification I am talking about is this:
// idToken comes from the client app
admin.auth().verifyIdToken(idToken)
.then(function(decodedToken) {
var uid = decodedToken.uid;
// ...
}).catch(function(error) {
// Handle error
});
Essentially, I want to know if Firebase performs ID token verification automatically when passing context using https call and therefore I can go ahead and trust that if the client has manipulated context, the https call will fail due to token verification failing. Or, do I need to explicitly do a manual ID token verification on the server every single time to check the integrity of context, since the client can easily insert a manipulated token using the browser's devtools or something like that.

Yes, the ID token is automatically included in the request and verified in the function. You don't have to write code to verify the toekn when using callable functions.

Related

How to securely generate a session token that can be independently verified by Auth instances

I'm currently working on making an authentication gRPC microservice using Rust and Tonic. The simple idea is that my service generates a token that can later be used to reference back to the UserID. I save this token and user relationship in a redis database so I can run multiple of these authentication services in tandem. I'm currently generating my tokens like this:
// Create a session for a username
pub fn create_session_id(username: &String) -> String {
// Generate unique ID
let id = Uuid::new_v4();
// Hash the ID and username for a unique session token
let mut hasher = Sha256::new();
hasher.update(id.to_string() + username);
// Return hexadecimal string encoding of hash
format!("{:X}", hasher.finalize())
}
I will be the first to admit it's a bit primitive. My problem is that there's nothing stopping a potential attacker with access to this database from inserting their own arbitrary token with an associated userid. This means I will have to be able to verify my token came from my authentication service. How do I generate a session token that can be independantly verified by any one of my auth instances without making it vunerable to forgery.
I have thought about implementing OAuth2, but I'm struggling to integrate that with the gRPC microservice architecture.
Any help would be appriciated.

Implement user authentication with gRPC

I'm looking to implement simple user authentication with my dart gRPC server + client and am struggling to find samples on how to achieve this properly.
So my problems are the following:
How do I add the user authentication data (JWT) to API calls that require authentication on the client?
How to I handle this data on the server?
I assume that on the client, metadata is the way to go, but is there a way to add the authentication data automatically for each call?
For the server, I assume that interceptors are the way to go, but how do I specify interceptors for specific services only (since not all API calls require authentication)?
is there a way to add the authentication data automatically for each call?
You can supply the default CallOptions with the options parameter in the generated client constructor.
You can use that to add authorization info to all your calls. If you need to perform async work for each call (for instance, to check if the token is still valid and optionally refresh it), you could add a MetadataProvider which gets invoked for each call.
how do I specify interceptors for specific services only (since not all API calls require authentication)?
The interceptor gets access to a ServiceMethod, which contains a name. So you could check that to only invoke an interceptor on some methods:
extension OnlyInterceptSome on Interceptor {
Interceptor limitTo(Set<String> endpoints) {
return (call, method) {
// Skip the check if we don't care about the method.
if (!endpoints.contains(method.name)) return null;
// Invoke the regular interceptor otherwise
return this(call, method);
};
}
}

How do I use HttpClientFactory with Impersonation? Or find another way to get a JWT token from a service based on a Windows Identity?

I have a regular ASP.Net Core web site that users access using Windows Authentication to determine which users can access which pages.
In order to render a page for the user, the site needs to call in to a series of web services to fetch various bits of data. These web services don't use Windows Authentication. Instead, they require the user's JWT Token.
So, our WebSite needs to exchange the user's Windows token for a JWT token. We have a special ExchangeToken web service that accepts a request using Windows Authentication, and returns the user's JWT Token.
The difficulty comes when I want WebSite to call this ExchangeToken web service. I need to call it using Impersonation, so that I get the user's JWT Token back. However, it doesn't appear to be possible to use HttpClient with Impersonation.
Initially, I had planned to do this in WebSite:
Repeatedly...
Impersonate the user
Instantiate an HttpClient
Call the TokenExchange service to get the JWT Token
Dispose the HttpClient
Stop impersonation
Return the token
However, according to what I've read, re-creating an HTTP client for every call is bad practice, and I should be using HttpClientFactory instead.
However, I don't see how this approach can work with Impersonation.
I tried this:
Use HttpClientFactory to create an HttpClient
Repeatedly...
Impersonate the user
Call the TokenExchange service to get the JWT Token
Stop impersonation
Return the token
However, what happens is that, despite the impersonation, all calls to the TokenExchange service are made with the same windows credentials - the credentials of the user who happens to access the web site first. AFAIK, this stems from the way that Windows Authentication works - it performs a token exchange the first time you use an HttpClient, and from then on, all calls for that client use the same token.
One option would be to create a separate client for each user... but I have about 7,000 users, so that seems a bit excessive!
Another option would be to trust the WebSite to fetch the tokens on behalf of the user, using its own account. The problem with this is that it entails trusting the WebSite. If it is compromised by an attacker, then I can't stop the attacker stealing JWT tokens for arbitrary user. Whereas, with the impersonation, the attacker still can't get a user's JWT token without first obtaining their Windows token.
So, is there a way to do impersonation + IHttpClientFactory together? Or is there a better way to approach all this?
(If it matters, my company has its own Windows servers - we're not in the cloud, yet)
To demonstrate the problem with the second approach, I made a test application. It doesn't actually use HttpClientFactory, but it does demonstrate the problem.
I started with a web site that just returns the user who made a call:
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class WhoController : ControllerBase
{
[HttpGet]
public ActionResult<string> Get()
{
return User.Identity.Name;
}
}
My client code works like this:
private void CallClient(HttpClient httpClient, string username, string password)
{
LogonUser(username, "MYDOMAIN", password, 2, 0, out IntPtr token);
var accessTokenHandle = new SafeAccessTokenHandle(token);
WindowsIdentity.RunImpersonated(
accessTokenHandle,
() =>
{
string result = httpClient.GetStringAsync("http://MyServer/api/who").Result;
Console.WriteLine(result);
});
}
And my test code invokes it like this:
public void Test()
{
var httpClient = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true });
CallClient(httpClient, "User1", "Password1");
CallClient(httpClient, "User2", "Password2");
}
As described above, I get the following written to the console:
User1
User1
What I want is:
User1
User2
TL;DR: NET Core is doing a lot to fight you on this approach under the hood.
Not entirely an answer on what to do, but hopefully helpful background on the HttpClientFactory approach, based on my understanding of the components.
First, from the ASP NET Core docs in regards to impersonation:
ASP.NET Core doesn't implement impersonation. Apps run with the app's
identity for all requests, using app pool or process identity. If the
app should perform an action on behalf of a user, use
WindowsIdentity.RunImpersonated in a terminal inline middleware in
Startup.Configure. Run a single action in this context and then close
the context.
RunImpersonated doesn't support asynchronous operations and shouldn't
be used for complex scenarios. For example, wrapping entire requests
or middleware chains isn't supported or recommended.
As you call out, there's a lot of progress NET Core has made around how HttpClient instances are handled to resolve socket exhaustion and the expensive operations around the underlying handlers. First, there's HttpClientFactory, which in addition to supporting creating named/typed clients with their own pipelines, also attempts to manage and reuse a pool of primary handlers. Second, there's SocketsHttpHandler, which itself manages a connection pool and replaces the previous unmanaged handler by default and is actually used under the hood when you create a new HttpClientHandler. There's a really good post about this on Steve Gordon's Blog: HttpClient Connection Pooling in NET Core. As you're injecting instances of HttpClient around from the factory, it becomes way safer to treat them as scoped and dispose of them because the handlers are no longer your problem.
Unfortunately, all that pooling and async-friendly reuse makes your particular impersonation case difficult, because you actually need the opposite: synchronous calls that clean up after themselves and don't leave the connection open with the previous credentials. Additionally, what used to be a lower-level capability, HttpWebRequest now actually sits on top of HttpClient instead of the other way around, so you can't even skip it all that well by trying to run the requests as a one off. It might be a better option to look into using OpenID Connect and IdentityServer or something to centralize that identity management and Windows auth and pass around JWT everywhere instead.
If you really need to just "make it work", you might try at least adding some protections around the handler and its connection pooling when it comes to the instance that is getting used to make these requests; event if the new clients per request are working most of the time, deliberately cleaning up after them might be safer. Full disclaimer, I have not tested the below code, so consider it conceptual at best.
(Updated Switched the static/semaphore to a regular instance since the last attempt didn't work)
using (var handler = new SocketsHttpHandler() { Credentials = CredentialCache.DefaultCredentials, PooledConnectionLifetime = TimeSpan.Zero, MaxConnectionsPerServer = 1 })
using (var client = new HttpClient(handler, true))
{
return client.GetStringAsync(uri).Result;
}

Enabling OAuth1 Support on a Jersey Jax-rs web service

I'd like to enable OAuth1 Provider support on my restful web service. Jersey supports this as described here Jersey OAuth1 Provider support.
I've been trying to register it as so:
public ApplicationConfig(){
super();
addRestResourceClasses(getMyResourceClasses());
register(new OAuth1ServerFeature(new DefaultOAuth1Provider(),"/oauth/access_token","/oauth/request_token"));
}
But, when I register the OAuth1ServerFeature, I get a 404 when trying to access my resources.
Can't seem to find any examples/tutorials implementing jersey oauth support anywhere!
Is there a simple component I can plug into my jax-rs service to enable oauth support?
I realise this thread is somewhat old - but having just got it work myself, I felt a reply was in order! Given time, I may even create a blog post with a fuller example. Be warned - this is not a short answer!
There is an absolute lack of examples on information on using the OAuth1 server (aka Provider) feature in Jersey - I can't remember a tech topic that revealed so little useful Google information. I almost passed on looking for another solution since it led me to think perhaps it didn't work. But, with some perseverance, I can say that not only is it usable, but it seems to work rather well. Plus of course, if you're already using Jersey for your REST API - you don't need any extra libs.
I am not an OAuth1 expert - and I'd strongly recommend some background reading for those attempting this. I am also assuming here you have Jersey working, understand things like ContainerRequestFilters, and also have some internal means to authorize users.
My examples also use the excellent JAX-RS OSGi connector - the only real difference is that where we use an OSGi bundle context to register the OAuth1 feature via an OSGI service, regular Jersey users will need to configure via their normal Application / Server config model.
Initialisation
You must create your OAuth1 feature - and give it a provider:
DefaultOAuth1Provider oap = new DefaultOAuth1Provider();
Feature oaFeature = new OAuth1ServerFeature(oap, "oauth1/request_token", "oauth1/access_token");
Don't forget to register oaFeature into Jersey!
The DefaultOAuth1Provider is entirely memory based - which was fine for us to start with. Many will want to persist access tokens for use across server restarts, which will require an extended subclass (or clean implementation)
Add in your Consumers Keys and Secrets
It took me a while to realise Consumers were not users but clients i.e. applications. The Jersey implementation will not work if you don't register keys and secrets for each consumer (aka client app) that wishes to connect
oap.registerConsumer("some-owner-id",
"abcdef" ,
"123456",
new MultivaluedHashMap<String,String> ());
You obviously would never hard-code these, and further would use some form of secure store for the secret (param 3).
If you do not add these you will not get any further.
OAuth protocol step 1 - get a request token
At this stage you are ready client side to get a request token - and here there is a perfectly good example on GitHub.
ConsumerCredentials consumerCredentials = new ConsumerCredentials("abcdef","123456");
//TODO - user proper client builder with real location + any ssl context
OAuth1AuthorizationFlow authFlow = OAuth1ClientSupport.builder(consumerCredentials)
.authorizationFlow(
"http://myhost:8080/myapi/oauth1/request_token",
"http://myhost:8080/myapi/oauth1/access_token",
"http://myhost:8080/myapi/oauth1/authorize")
.build();
String authorizationUri = authFlow.start();
System.out.println("Auth URI: " + authorizationUri);
Obviously you would change URLs to point to your server and - crucially - the client needs to use the same Conumer Key and Secret you registered in the server.
You will get back a response with an oauth_token string in it e.g.
http://myhost:8080/myapi/oauth/authorize?oauth_token=a1ec37598da
b47f6b9d770b1b23a5f99
OAuth protocol step 2 - authorize the user
As you will read in any article, actual user Authorization is outside of the scope of OAuth1 - at this stage you must invoke your servers auth process whatever that is.
However!!!! What is not outside the OAuth1 scope is what your server needs to do if the user authorizes successfully. You must tell your DefaultOAuth1Provider about the successful auth:
// Dummy code - make out like we're auth'd
Set<String> dummyRoles = new HashSet<> (Arrays.asList( new String[] { "my-role-1", "my-role-2" }));
DefaultOAuth1Provider.Token tok1 = getRequestToken("a1ec37598da
b47f6b9d770b1b23a5f99");
String verifier = authorizeToken(tok1, new Principal()
{
public String getName()
{
return "my-user";
}
},
dummyRoles);
System.out.println("***** verifier: " + verifier);
Note the request token string is that from step 1. Obviously a real implementation would pass a real Principal and set of roles for the authorized user.
Also, of course, printing out the verifier is not much use - you need to get that back to your client in some way, either via an independent channel or possibly as a header in the auth response - which maybe would need to be encrypted for added protection.
OAuth protocol step 3 - swap the request token for an access token
Once the client receives or has the verifier entered manually, it can finalize the process and swap the request token for an access token e.g.
String verifier = System.console().readLine("%s", "Verifier: ");
final AccessToken accessToken = authFlow.finish(verifier);
System.out.println("Access token: " + accessToken.getToken());
Again, not a realistic example - but it shows the process.
If your OAuth1Provider saves access tokens to some persistent store on the server, you can re-use any access token returned here on a future session without going through all the previous steps.
That's it - you then just need to make sure every request the client creates from this point on in the process makes use of that access token.

How to get Authorization Token

I secured my Web Api using Token Based authentication. All works well, but for now before making an api call, I request for a token by making separate api call which will return me the token for making further request. What I am wondering is there any possible way that I can generate token on client side which will be decrypted on the server?
OR
What you think, I am on the right track?
Here is my jQuery Code
$.when($.get("/api/service/GetToken"))
.done(function (token) {
doAjaxCall("GET", "/api/service/GetAllJobsStatusCount/1/admin", "{}", "json", token, function (data) {
console.log(data);
});
});
Here is my method which will return me token
[Api.HttpGet]
public string GetToken()
{
var authorizeToken = "apikey";
return Rsa.Encrypt(authorizeToken);
}
Please suggest
A single token only works for authentication if an encrypted connection (HTTPS) is used - otherwise, MITMs can read the token and steal the identity. If everything is done using secure connections, it doesn't matter who calculates the token, as long as it is exchanged before any authentications, so both partners know it beforehand - otherwise there would be no way to know if it's valid or not. Note however that the token can always only prove that the client is the one that the token was given to (or recevied from) in the first place, you still don't know anything else (for example if the other person was who he claimed when te exchange was made).
If not using secure connections, I'd suggest using public-key encryption. If not encrypting the whole request, calculate a signature and sign it with the private key clientside, verify it server-side. In this case, the client MUST calculate the private and the public key and hand the public key over to the server, as the server should never know the client's private key (and it shouldn't ever be sent over a unencrypted connection either, after all that'S the whole point of using PKI).
For another approach (that is widely used, but not extraordinary secure), see OAuth.
[Edit]: Addition based on comment:
To verify a client, every request that accesses valuable data has to be authenticated. That means: the client needs to send something that is able to prove that it is who it claims to be along with EVERY request (as HTTP is a stateless protocol).
The usual way is to exchange some secret once, that is valid for either a limited time or forever, and only known to the client and the servet. That way, the server can verify idientities by checking if the client sent the token it was given. Expiring tokens after improves security, but when a client requests a knew one, there is no way to tell if it is the same client that used the old one (unless the request is validated by the old token and that one is still valid).
If ther client calculates the token, that deosn'T change anything - it sends it once and tells the server who it is, then every request that carries the same token does most likely come from the same client. There is only one additional thing the server has to do here: If two clients calculate the same token, the server has to reject the token from the second client and request another one. Otherwise, clients cannot be distinguished anymore.