Does connecting HashPack wallet mean the application can read my private key? - hedera-hashgraph

When we connect our HashPack wallet, does it mean I allowed the application to read my private key?
I think it is not necessary to read the private key of my wallet to log in.

No, connecting signing wallets like HashPack or Blade does not read you private keys from the application. Those wallets implement a signer/provider architecture. See the HIP (https://hips.hedera.com/hip/hip-338) and documentation (https://docs.hedera.com/hedera/docs/signature-provider) for more details.
A provider is like a tunnel that enables sending transactions from the application to the wallet for signing. That way the signer (the account in the wallet) can authorize the transaction while keeping the private keys securely in the wallet.
Here's a code sample that signs and executes a smart contract function using the signer in a HashPack wallet:
//Execute a contract function (transfer)
const contractExecTx = await new ContractExecuteTransaction()
.setContractId(contractId)
.setGas(3000000)
.setFunction("tokenAssoTrans", new ContractFunctionParameters().addInt64(50))
.freezeWithSigner(signer);
const contractExecSign = await contractExecTx.signWithSigner(signer);
const contractExecSubmit = await contractExecSign.executeWithSigner(signer);

Related

GCP Server to Server Authentication with Service Account

I'm trying to authenticate a request from my Google Cloud Function to my API on App Engine (Standard environment).
I have something working, but I'm new to OAuth2 and am looking for a sanity check.
In my Cloud Function, I send an authenticated request to my API doing the following:
import { GoogleAuth } from 'google-auth-library';
// Send Request Code:
const auth = new GoogleAuth();
const tokenClient = await auth.getIdTokenClient(`/protectedEndpoint`);
await tokenClient.request({
url: `https://${process.env.GCLOUD_PROJECT}.appspot.com/protectedEndpoint`,
method: 'POST',
});
In the API (on App Engine), I do the following:
import { GoogleAuth } from 'google-auth-library';
// Handle Request Code:
const token = <Bearer token parsed from request headers>
const googleAuth = new GoogleAuth();
const tokenClient = await googleAuth.getIdTokenClient('');
const loginTicket = await tokenClient.verifyIdToken({
idToken: token,
audience: '/protectedEndpoint',
});
if (loginTicket.getUserId() !== process.env.SERVICE_ACCOUNT_ID)) {
throw new Error('Unauthenticated Service Account');
}
return 'Successful Authentication'
Note: In both cases, I'm using Google's default application credentials to initialize the GoogleAuth client. (my Default App Engine service account)
This all works. My function sends a request to my API, and my API is able to parse the bearer token and tell me that it came from my approved service account... but I'm not 100% confident that this is actually secure. Is it possible for someone to spoof my service account without having its credentials?
Thanks in advance!
Is it possible for someone to spoof my service account without having
its credentials?
A precise answer requires the specification of time. Given enough time and processing power, any authentication/authorization/encryption/hashing/signing method can be broken.
A Google service account contains an RSA 2048-bit private key. Current guesstimates are 300 trillion years to break RSA 2048 bit encryption. With the rapid advances in computers, let's assume your data will probably not be of any use/value by the time RSA is broken.
The private key is used to sign a JWT. The Signed JWT is used to request OAuth Access/Identity Tokens.
Spoofing would require signing with the same private key. Therefore, spoofing is not possible with today's technology.
Stealing/leaking the private key or the generated OAuth tokens is the only realistic method today.

Create GraphClient with UsernamePasswordProvider with client secret

I created a desktop application that talks to Graph API (Beta).
In the development version, I deployed it on the application I created myself on Azure AD. As one of the requirements is not to show login dialog when using the application, I decided to go with UsernamePasswordProvider, which I provide the user name and password of my account and everything works fine (I didn't put client secret in my application).
When I'm about to deploy it on customer's network, I asked the admin and he provides me the service user name, password, tenant ID, client ID, and a client secret.
I tried using Postman to check if I can get an access token from those info and I can only if I provide the client secret along with user name and password.
When I'm back to the code, UsernamePasswordProvider's constructor accepts IPublicClientApplication which has no option to create the application with client secret.
I understand that there is a reason behind as the secret can be easily stolen if the application got decompiled but, if I'm (in fact, my client) not serious about this, is there any way to initialize the GraphClient by putting user name, password, and client secret together for authentication?
Important, the Username / Password flow is not recommended because your application asking a user for their password is not secure. For more information about this problem, see this article.
If you still would like to use it, you could put username and password of that user in Key Vault and fetch those in your code.
string keyVaultName = Environment.GetEnvironmentVariable("KEY_VAULT_NAME");
var kvUri = "https://" + keyVaultName + ".vault.azure.net";
var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
var secret = client.GetSecret(secretName); // get username or password here
I recommend you to use ClientCredentialsProvider, it enables service applications to run without user interaction.

What is the use of Client secrets?

We are using EntityFramework Core to store configuration data. What is use of store Client secrets along with clients? Or can we add/modify clients secrets later on?
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" },
AccessTokenLifetime=3600
}
You will need the secret to access the Token Endpoint when programmatically requesting tokens.
For example, you can use the IdentityModel library:
var client = new TokenClient(
doc.TokenEndpoint,
"client_id",
"secret");
var response = await client.RequestClientCredentialsAsync("scope");
var token = response.AccessToken;
Although Gavin Sutherland's answer explains how to use client secrets, I believe the question is more about why they exist. Basically, think of the client ID and client secret as a username / password enabling a specific client application to initiate OpenID Connect requests (with the other IdentityServer Client configuration elements defining which requests are permitted).
You can change secrets, but synchronizing the secret between IdentityServer and the client application is a manual process. (It isn't like certain key rotation scenarios where you can have more than one secret active for awhile, removing the old one after all clients have updated, if that's what you're thinking.)
If you look at any OIDC / OAuth2 third-party identity provider (Google, Facebook, etc.) they all operate by issuing client IDs and secrets associated with specific clients (normally this means specific domains).

How to Authenticate MS Teams User Against Azure AD

I am trying to create a bot which would be deployed into MS Teams (and Skype for Business). I see when a user interacts with the bot they are provided with a channelData.tenant.id and the Bot Framework docs say that this is the "The tenant ID for the user." I was wondering if I can use this (or another piece of information coming from the inbound message) to authenticate the user against my Azure AD? Also, would this require me to authenticate the user via an authentication flow like is done with the AuthBot?(https://github.com/MicrosoftDX/AuthBot)
Any help would be great!
You have the tenant.id provided in channelData yes, so you could use it to make some customs requests like with Graph API.
For MS Teams, you can also get more information by calling GetConversationMembersAsync and call AsTeamsChannelAccount method on the members that you got (this method is included in Microsoft.Bot.Connector.Teams NuGet package)
Sample:
// Fetch the members in the current conversation
var connector = new ConnectorClient(new Uri(context.Activity.ServiceUrl));
var members = await connector.Conversations.GetConversationMembersAsync(context.Activity.Conversation.Id);
// Concatenate information about all the members into a string
var sb = new StringBuilder();
foreach (var member in members.Select(m => m.AsTeamsChannelAccount()))
{
sb.AppendLine($"GivenName = '{member.Name}', Email = '{member.Email}', User Principal Name '{member.UserPrincipalName}', AAD ObjectId '{member.ObjectId}', TeamsMemberId = '{member.Id}'");
}
// Post the member info back into the conversation
await context.PostAsync($"People in this conversation: {sb.ToString()}");
With this call you will have additional interesting values: Email and ObjectId (which is the user's Azure AD object ID).
As a conclusion, you still have to log your user if you need to do some authenticated logic, but in MS Teams case you have more information and ways to do it.

How do I protect my API that was built using Google Cloud Endpoints?

The API is a backend to a mobile app. I don't need user authentication. I simply need a way to secure access to this API. Currently, my backend is exposed.
The documentation seems to only talk about user authentication and authorization, which is not what I need here. I just need to ensure only my mobile app can talk to this backend and no one else.
Yes, you can do that: use authentication to secure your endpoints without doing user authentication.
I have found that this way of doing it is not well documented, and I haven't actually done it myself, but I intend to so I paid attention when I saw it being discussed on some of the IO13 videos (I think that's where I saw it):
Here's my understanding of what's involved:
Create a Google API project (though this doesn't really involve their API's, other than authentication itself).
Create OATH client ID's that are tied to your app via its package name and the SHA1 fingerprint of the certificate that you will sign the app with.
You will add these client ID's to the list of acceptable ID's for your endpoints. You will add the User parameter to your endpoints, but it will be null since no user is specified.
#ApiMethod(
name = "sendInfo",
clientIds = { Config.WEB_CLIENT_ID, Config.MY_APP_CLIENT_ID, Config.MY_DEBUG_CLIENT_ID },
audiences = { Config.WEB_CLIENT_ID }
// Yes, you specify a 'web' ID even if this isn't a Web client.
)
public void sendInfo(User user, Info greeting) {
There is some decent documentation about the above, here:
https://developers.google.com/appengine/docs/java/endpoints/auth
Your client app will specify these client ID's when formulating the endpoint service call. All the OATH details will get taken care of behind the scenes on your client device such that your client ID's are translated into authentication tokens.
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience( ctx, Config.WEB_CLIENT_ID );
//credential.setSelectedAccountName( user ); // not specify a user
Myendpoint.Builder builder = new Myendpoint.Builder( transport, jsonFactory, credential );
This client code is just my best guess - sorry. If anyone else has a reference for exactly what the client code should look like then I too would be interested.
I'm sorry to say that Google doesn't provide a solution for your problem (which is my problem too).
You can use their API key mechanism (see https://developers.google.com/console/help/new/#usingkeys), but there is a huge hole in this strategy courtesy of Google's own API explorer (see https://developers.google.com/apis-explorer/#p/), which is a great development tool to test API's, but exposes all Cloud Endpoint API's, not just Google's services API's. This means anyone with the name of your project can browse and call your API at their leisure since the API explorer circumvents the API key security.
I found a workaround (based on bossylobster's great response to this post: Simple Access API (Developer Key) with Google Cloud Endpoint (Python) ), which is to pass a request field that is not part of the message request definition in your client API, and then read it in your API server. If you don't find the undocumented field, you raise an unauthorized exception. This will plug the hole created by the API explorer.
In iOS (which I'm using for my app), you add a property to each request class (the ones created by Google's API generator tool) like so:
#property (copy) NSString *hiddenProperty;
and set its value to a key that you choose. In your server code (python in my case) you check for its existence and barf if you don't see it or its not set to the value that your server and client will agree on:
mykey,keytype = request.get_unrecognized_field_info('hiddenProperty')
if mykey != 'my_supersecret_key':
raise endpoints.UnauthorizedException('No, you dont!')
Hope this puts you on the right track
The documentation is only for the client. What I need is documentation
on how to provide Service Account functionality on the server side.
This could mean a couple of different things, but I'd like to address what I think the question is asking. If you only want your service account to access your service, then you can just add the service account's clientId to your #Api/#ApiMethod annotations, build a GoogleCredential, and invoke your service as you normally would. Specifically...
In the google developer's console, create a new service account. This will create a .p12 file which is automatically downloaded. This is used by the client in the documentation you linked to. If you can't keep the .p12 secure, then this isn't much more secure than a password. I'm guessing that's why this isn't explicitly laid out in the Cloud Endpoints documentation.
You add the CLIENT ID displayed in the google developer's console to the clientIds in your #Api or #ApiMethod annotation
import com.google.appengine.api.users.User
#ApiMethod(name = "doIt", scopes = { Constants.EMAIL_SCOPE },
clientIds = { "12345678901-12acg1ez8lf51spfl06lznd1dsasdfj.apps.googleusercontent.com" })
public void doIt(User user){ //by convention, add User parameter to existing params
// if no client id is passed or the oauth2 token doesn't
// match your clientId then user will be null and the dev server
// will print a warning message like this:
// WARNING: getCurrentUser: clientId 1234654321.apps.googleusercontent.com not allowed
//..
}
You build a client the same way you would with the unsecured version, the only difference being you create a GoogleCredential object to pass to your service's MyService.Builder.
HttpTransport httpTransport = new NetHttpTransport(); // or build AndroidHttpClient on Android however you wish
JsonFactory jsonFactory = new JacksonFactory();
// assuming you put the .p12 for your service acccount
// (from the developer's console) on the classpath;
// when you deploy you'll have to figure out where you are really
// going to put this and load it in the appropriate manner
URL url = getClass().class.getResource("/YOURAPP-b12345677654.p12");
File p12file = new File(url.toURI());
GoogleCredential.Builder credentialBuilder = new GoogleCredential.Builder();
credentialBuilder.setTransport(httpTransport);
credentialBuilder.setJsonFactory(jsonFactory);
//NOTE: use service account EMAIL (not client id)
credentialBuilder.setServiceAccountId("12345678901-12acg1ez8lf51spfl06lznd1dsasdfj#developer.gserviceaccount.com"); credentialBuilder.setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/userinfo.email"));
credentialBuilder.setServiceAccountPrivateKeyFromP12File(p12file);
GoogleCredential credential = credentialBuilder.build();
Now invoke your generated client the same way
you would the unsecured version, except the builder takes
our google credential from above as the last argument
MyService.Builder builder = new MyService.Builder(httpTransport, jsonFactory, credential);
builder.setApplicationName("APP NAME");
builder.setRootUrl("http://localhost:8080/_ah/api");
final MyService service = builder.build();
// invoke service same as unsecured version

Resources