How to store received WebAuthn private key in Selenium tests (java) - java

I am trying to build a selenium test (java) that goes through WebAuthn authentication. Thanks to thread here I am able to retrieve the private key. The question is how to store and reuse it? I tried to store private key to the disk
final PKCS8EncodedKeySpec privateKey = authenticator.getCredentials().get(0).getPrivateKey();
File outputFile = new File("./private.key");
Files.write(outputFile.toPath(), privateKey.getEncoded());
Then when I run test case for the same user I try to load it and create an instance of Credential like this:
Credential credential = Credential.createNonResidentCredential(
id, "null", new PKCS8EncodedKeySpec(key), /*signCount=*/1);
and load the credentials to my driver:
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions();
options.setTransport(VirtualAuthenticatorOptions.Transport.INTERNAL)
.setHasResidentKey(true)
.setProtocol(VirtualAuthenticatorOptions.Protocol.CTAP2);
VirtualAuthenticator authenticator = ((HasVirtualAuthenticator) driver).addVirtualAuthenticator(options);
authenticator.addCredential(credential);
but the credential is got refused. Unfortunately I haven't found any information on how to transfer the credentials between the sessions. What is the usual workflow in this case? It should be quite common scenario I think

I have run into a similar issue, my use case is webauthn registration and login on different tabs ( = I have to transfer the credentials).
After registration I save the credentials of the user:
VirtualAuthenticator virtualAuthenticator = ((HasVirtualAuthenticator) Driver.get()).addVirtualAuthenticator(new VirtualAuthenticatorOptions());
// setup happens
Credential credential = authenticator.getCredentials().get(0);
userAccount.addCredential(credential);
On login I do this:
VirtualAuthenticator virtualAuthenticator = ((HasVirtualAuthenticator) Driver.get()).addVirtualAuthenticator(new VirtualAuthenticatorOptions());
Credential credential = userAccount.getFido2Credential();
Credential c = Credential.createNonResidentCredential (credential.getId(),"<whatever is your rpid(~domain)>", credential.getPrivateKey(), credential.getSignCount());
authenticator.addCredential(c);
My issue was that the rpid was null in the credential object and the authenticator's addCredential method failed because of this.

Related

How can I generate a licensed download link in azure storage blob

I can get a link by those code, and it could download a file successfully.
BlobContainerSasPermission blobContainerSasPermission = new BlobContainerSasPermission()
.setReadPermission(true)
.setWritePermission(true)
.setListPermission(true);
BlobServiceSasSignatureValues builder = new BlobServiceSasSignatureValues(OffsetDateTime.now().plusDays(1), blobContainerSasPermission)
.setProtocol(SasProtocol.HTTPS_ONLY);
BlobClient client = new BlobClientBuilder()
.connectionString("connection string")
.blobName("")
.buildClient();
String blobContainerName = "test";
return String.format("https://%s.blob.core.windows.net/%s?%s",client.getAccountName(), blobContainerName, client.generateSas(builder));
but everyone could download the file by this link, I want the file could be downloaded by the one who is authorized. Is there any code or azure's setting(such as AD?) could implement this? Thanks
-------------------------update------------------------
I find a doc,and there gives a reason.
Note: This method call is only valid when using TokenCredential in this object's HttpPipeline.
but there is only BasicAuthenticationCredential which implements TokenCredential in my imported package. This is my mvn.
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.11.0-beta.2</version>
</dependency>
And I tried this
String userName = "userName";
String password = "password";
BasicAuthenticationCredential basicAuthenticationCredential = new BasicAuthenticationCredential(userName,password);
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().endpoint(endpoint).credential(basicAuthenticationCredential).buildClient();
then I got this Status code 401, (InvalidAuthenticationInfo)
Gods, help me!
A SAS token for access to a container, directory, or blob may be
secured by using either Azure AD credentials or an account key. A SAS
secured with Azure AD credentials is called a user delegation SAS.
Microsoft recommends that you use Azure AD credentials when possible
as a security best practice, rather than using the account key, which
can be more easily compromised. When your application design requires
shared access signatures, use Azure AD credentials to create a user
delegation SAS for superior security.
Generating SAS query parameters with UserDelegationKey
The following sample generates SAS query parameters for an Azure storage container.
BlobSasPermission blobPermission = new BlobSasPermission()
.setReadPermission(true)
.setWritePermission(true);
// We are creating a SAS to a container because only container name is set.
BlobServiceSasSignatureValues builder = new BlobServiceSasSignatureValues()
.setProtocol(SasProtocol.HTTPS_ONLY) // Users MUST use HTTPS (not HTTP).
.setExpiryTime(OffsetDateTime.now().plusDays(2))
.setContainerName("my-container")
.setPermissions(blobPermission);
// Get a user delegation key after signing in with Azure AD
UserDelegationKey credential = new UserDelegationKey();
String account = "my-blob-storage-account";
BlobServiceSasQueryParameters sasQueryParameters = builder.generateSasQueryParameters(credential, account);
Check out Create a user delegation SAS and this for implementation details.
It looks like there is no way to implement this.
This is the answer.

Hangout Chat API authentication fails with Default Service account

I'm testing the new Google Chat bot that should post messages asynchronously, thus I must use GoogleCredentials to authenticate requests.
It works perfectly fine when I use GoogleCredentials.getApplicationDefault() while working locally, but when I deploy the bot to Cloud Run it stops.
idiomatic secure way (does not work)
The HangoutsChat is created the following way:
var credentials = GoogleCredentials.getApplicationDefault()
.createScoped(CHAT_BOT_SCOPE);
var credentialsAdapter = new HttpCredentialsAdapter(credentials);
var chat = new HangoutsChat.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
JacksonFactory.getDefaultInstance(),
credentialsAdapter)
.setApplicationName(BOT_NAME)
.build();
So in order to work locally, I set GOOGLE_APPLICATION_CREDENTIALS and point it to the service account key like this: export GOOGLE_APPLICATION_CREDENTIALS=./credentials/adc.json. While in the cloud environment I expect the same service account to just work, but if not set explicitly from the file, the credentials throw insufficientPermissions error with PERMISSION_DENIED.
workaround (works fine)
As a workaround, I'm creating credentials from the service account stored in Secret Manager like this:
var serviceAccount = Secrets.chatServiceAccount();
var credentials = GoogleCredentials.fromStream(streamFrom(serviceAccount))
.createScoped(CHAT_BOT_SCOPE);
var credentialsAdapter = new HttpCredentialsAdapter(credentials);
var chat = new HangoutsChat.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
JacksonFactory.getDefaultInstance(),
credentialsAdapter)
.setApplicationName(BOT_NAME)
.build();
return chat;
private static InputStream streamFrom(String data) {
return new ByteArrayInputStream(data.getBytes(Charset.defaultCharset()));
}
The question is: is anyone able to use default credentials for HangoutsChat API? Is it even possible or I am missing something here?

getting "401 Unauthorized" while trying to authenticated with service account

So here is the problem: Ive recently made this post.
Solution I've mentionned worked for one token and one API but when I tried to handle two APIs with two token (gmail and Sheets API) it failed.
So what I'm trying to do now is make the two work so I told myself "Hey let's create a service account". Even if I don't really understand the differences between both methods. Service account seems to prevent from having a consent screen (Am I right?).
I've crawled the web for answers but all of them seems to fail.
I've refreshed token, used GoogleCredential instead of Credential, created new key etc... one thing though I didn't tried is to use Gsuite account I'm using a basic account.
So now I'm at the point where I've created a new p12 file and instantly I get the 401 error. I will share my code for a better understanding.
my mail class
public class mailService {
private static final String APPLICATION_NAME = "AHS";
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final String TOKENS_DIRECTORY_PATH = "tokens";
//I've added sheet scope as it is activated in my project
private static final Collection<String> SCOPES = Arrays.asList(GmailScopes.GMAIL_SEND, GmailScopes.GMAIL_LABELS, SheetsScopes.SPREADSHEETS);
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException, GeneralSecurityException {
File sa = new File("WEB-INF/mykeyfile.p12");
Credential credential = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(
"myapp#appspot.gserviceaccount.com")
.setServiceAccountScopes(SCOPES)
.setServiceAccountPrivateKeyFromP12File(sa)
.setServiceAccountUser("myemailadress#gmail.com")
.build();
//credential.refreshToken();
return (credential);
}
public static Gmail getService() throws GeneralSecurityException, IOException {
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
/*Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();*/
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, null)
.setHttpRequestInitializer(getCredentials(HTTP_TRANSPORT)).setApplicationName(APPLICATION_NAME).build();
return (service);
}
...
So to give you a better understanding, I'm creating a web app using Angular and Google app engine. I wan't to use Gmail API to send mail from my account, also I'm using sheets API to read/write from/to a spreadsheet. Just to be clear I have a secret file for the google-sign-in (for the user of the web app) but there this is server side code and I don't wan't user to see a consent screen.
I'm also asking myself if I need to use gcloud in order to activate service account.
I'm running (for the moment) my server locally using Eclipse and google app engine plugin.
if you need other code or precisions for better understanding of the problem let me know
Gmail dosent support service accounts unless its a gsuite account and you set up domain wide deligation.
If you check the documentation you will only see information about using Oauth2 not server account this is because Google only documents things that are supported not those that aren't.
Sheets does support service accounts just remembered that you need to pre-authorization on the service a account. That is done via sharing the sheet with the service account like you would any other user using the service accounts email address.

To sign into this application the account must be added to the <TenantID> directory

I am trying to authenticate and app and get bearer token for further use. I get the error which is title of this thread.
Another thread describes same thing except that my code is in Java. the workaround is to use certificate method.
"To sign into this application the account must be added to the domain.com directory"
Can someone please describe detailed steps for this workaround :- certificate method
Or how can i fix the below code with any other method
Or is any other method to achieve this whole task
Here is my code
private final static String AUTHORITY = "https://login.microsoftonline.com/<tenantId>/OAuth2/Authorize";
private final static String CLIENT_ID = "<Client_Id>";
private final static String CLIENT_SECRET = "<Secret>";
public static void main(String args[]) throws Exception {
try (BufferedReader br = new BufferedReader(new InputStreamReader(
System.in))) {
String username = CLIENT_ID;
String password = CLIENT_SECRET;
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future = context.acquireToken(
"https://graph.microsoft.com", CLIENT_ID, username, password,
null);
result = future.get();
}
finally {
service.shutdown();
}
}
I have registered my app in AAD app registration. The Client_Id is App Id and Secret is a key in the above code
In case, you want to make the code work in it's current form without any workaround, check for following things -
Make sure you have the correct tenantId GUID specified in the first line of code.
Steps to get tenantid -
Login to Azure Portal, Navigate to your Azure AD, Go to properties like in screenshot below and Directory ID should give you the GUID.
private final static String AUTHORITY = "https://login.microsoftonline.com/<tenantId>/OAuth2/Authorize";
Make sure that Username you are using, is for a user that belongs to your AzureAD tenant.
One possible reason could be if you're using a Microsoft account like xyz#outlook.com or hotmail.com etc. Try using an account that is created in this Azure AD like xyz#yourtenantdomain.onmicrosoft.com or any other verified domain that your tenant uses.
Future<AuthenticationResult> future = context.acquireToken("https://graph.microsoft.com", CLIENT_ID, username, password, null);
If you want to use certificate credentials to authenticate your app, you could refer to this article.
Steps:
1.Uploading the certificate file
2.Updating the application manifest
Code sample(it uses C#, you could refer):
Authenticating to Azure AD in daemon apps with certificates
It also shows how you can create a self-signed certificate using the New-SelfSignedCertificate Powershell command. You can also take advantage and use the app creation scripts to create the certificates, compute the thumbprint, and so on.

Upload videos to Youtube from my web server in Java

My goal is to upload videos that are uploaded to my web server to Youtube on my own channel, not the users' Youtube account (my web server is acting as a proxy).
I found the sample code for uploading video to Youtube here with the credential acquired this way. The problem that I have with this sample is that it writes to disk the credential, and it opens an http server. Since my web server can potentially have a lot of users uploading their videos concurrently, the credential file location has to be dynamic, and multiple binding to the same http port is not possible. Further more, after searching through other writing about uploading to Youtube, I think this approach is for users uploading to their Youtube account.
Could you share your experiences/code sample/solutions for my scenario? In short I am just trying to automate the process of me opening up Youtube dashboard, and uploading videos to a channel in my Youtube.
In general, starting at API V3, Google prefers OAuth2 over other mechanism, and uploading a video (or any other action that modifies user data) requires OAuth2.
Fortunately, there is a special kind of token called refresh token to the rescue. Refresh token does not expire like normal access token, and is used to generate normal access token when needed. So, I divided my application into 2 parts:
The 1st part is for generating refresh token, which is a Java desktop app, meant to be run by a user on a computer. See here for sample code from Google.
The 2nd part is is part of my web application, which uses a given refresh token to create a credential object.
Here is my implementation in Scala, which you can adapt to Java version easily:
For generating a refresh token, you should set the accessType to offline for the authorization flow. Note: if a token already exists on your system, it won't try to get new token, even if it does not have refresh token, so you also have to set approval prompt to force:
def authorize(dataStoreName: String, clientId: String, clientSecret: String): Credential = {
val builder = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT,
JSON_FACTORY,
clientId,
clientSecret,
Seq(YouTubeScopes.YOUTUBE_UPLOAD)
)
val CREDENTIAL_DIRECTORY = s"${System.getProperty("user.home")}/.oauth-credentials"
val fileDataStoreFactory = new FileDataStoreFactory(new java.io.File(CREDENTIAL_DIRECTORY))
val dataStore: DataStore[StoredCredential] = fileDataStoreFactory.getDataStore(dataStoreName)
builder.setCredentialDataStore(dataStore).setAccessType("offline").setApprovalPrompt("force")
val flow = builder.build()
val localReceiver = new LocalServerReceiver.Builder().setPort(8000).build()
new AuthorizationCodeInstalledApp(flow, localReceiver).authorize("user")
}
val credential = authorize(dataStore, clientId, clientSecret)
val refreshToken = credential.getRefreshToken
For using the refresh token on the server, you can build a credential from a refresh token:
def getCredential = new GoogleCredential.Builder()
.setJsonFactory(JSON_FACTORY)
.setTransport(HTTP_TRANSPORT)
.setClientSecrets(clientId, clientSecret)
.build()
.setRefreshToken(refreshToken)
I have have bypassed the whole AuthorizationCodeInstalledApp authorize() method and created a new subclass which bypasses the jetty server implementation process.
The methods are as follows
getAuthorizationFromStorage : Get access token from stored credentials.
getAuthorizationFromGoogle : Get the authentication with the credentials from Google creates the url that will lead the user to the authentication page and creating a custom defined name-value pair in the state parameter. The value should be encoded with base64 encoder so we can receive the same code redirected from google after authentication.
saveAuthorizationFromGoogle : Save the credentials that we get from google.
Create the GoogleAuthorizationCodeFlow object from the credentialDatastorfrom the response received from the google after authentication.
Hit google to get the permanent refresh-token that can be used to get the accesstoken of the user any time .
Store the tokens like accesstoken and refreshtoken in the filename as userid
Checkout the code Implementation here

Categories