I am creating an app that works like an authenticator for the website logins, I have setup a server of https://www.strongkey.com/ and here is the samples I am following https://github.com/StrongKey/fido2/tree/master/sampleapps/java/sacl/mobile/android.
But I am not aware how to achieve this facility, What my application is doing is
Hitting the pre register api and getting the challenge to register
sample response
{"Response":{"rp":{"name":"FIDOServer","id":"fidoidqa.com"},"user":{"name":"devendra","id":"s5wXaholuoVwk86KQ0d_hmIxOkQPNS-bBBes8X4Cex8","displayName":"devendraLiapC"},"challenge":"COJ03Ch_6KDjlvnZ1jg_Qw","pubKeyCredParams":[{"type":"public-key","alg":-7},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},{"type":"public-key","alg":-8},{"type":"public-key","alg":-47},{"type":"public-key","alg":-257},{"type":"public-key","alg":-258},{"type":"public-key","alg":-259},{"type":"public-key","alg":-37},{"type":"public-key","alg":-38},{"type":"public-key","alg":-38}],"excludeCredentials":[{"type":"public-key","id":"NEVDOUQzNkMzMDBEM0U3MS1FNDczNTQ3QUVDRDQ1ODDRELTk1MEJFOTM2NTI5MEIxNjctMTIxNkNFQjY1ODIzQTI5OQ","alg":-7},{"type":"public-key","id":"MUUzMDY0RkNGQUZEOTM5Ni1FMzlFOUM2MkUwOTQE4NzcwLTA0NzUyMEFBREM0ODUwM0UtMEU4ODdFOEFCRjFCMDE3QQ","alg":-7},{"type":"public-key","id":"hhkXnYmUiu_bzLy5HPHJvZs6TQA-302jRdeLHBgpL40","alg":-257}],"attestation":"direct"}}
Then with this response, I am creating the preregisterchallenge,
var preregisterChallenge = PreregisterChallenge() val authenticatorSelectionCriteria = AuthenticatorSelectionCriteria() authenticatorSelectionCriteria.authenticatorAttachment = "Android" authenticatorSelectionCriteria.isRequireResidentKey = true authenticatorSelectionCriteria.userVerification = "required" val authSelectionJson = Gson().toJson(authenticatorSelectionCriteria) val myCustomArray: JsonArray = Gson().toJsonTree(userData.Response?.pubKeyCredParams).asJsonArray preregisterChallenge.apply { id = 100 uid = 1001 did = 1003 rpid = userData.Response?.rp?.id userid = "1001" username = "devendra" displayName = "devendra" challenge = userData.Response?.challenge authenticatorSelectionJSONObject = JSONObject(authSelectionJson) authenticatorSelection = authSelectionJson publicKeyCredentialParams = myCustomArray.toString() credParamsJSONArray = JSONArray(myCustomArray.toString()) }
And then doing like this
val publicKeyCredential = AuthenticatorMakeCredential.execute( ContextWrapper(context), preregisterChallenge, "fidoidqa.com" ) as PublicKeyCredential
I am getting the publickKeyCredential without any error
Now I want to attest this key with biometric humen presence with this public key, for example now user will touch the biometric to verify this public key and then I will call the register api.
How can I attest the public key credential with the biometric fingerprint and want a signed public key credential for future verification.
Hope this is well explained, Please help me in understanding the flow or process.
Thanks in advance
Related
For creating firebase custom auth tokens, I am using third party JWT library (https://github.com/jwtk/jjwt)
In this library, there is an option to add firebase Custom Token Claims like (alg, iss,sub,aud,iat etc.)
All firebase information is available at https://firebase.google.com/docs/auth/admin/create-custom-tokens#create_custom_tokens_using_a_third-party_jwt_library
private_key = "-----BEGIN PRIVATE KEY-----... -----END PRIVATE KEY-----";
I had passed private key in signWith method with encoding in base64
val encodeKey = Base64.encode(privateKey.toByteArray(), android.util.Base64.DEFAULT)
val jwt = Jwts.builder().setIssuer("firebase-adminsdk-kywht#...")
.setSubject("firebase-adminsdk-kywht#...")
.setAudience("https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit")
.setExpiration(calendar.time) //a java.util.Date
.setIssuedAt(Date())
.setId(UUID.randomUUID().toString())signWith(SignatureAlgorithm.RS512 ,encodeKey).compact()
I have used the above code but it did not work.
Anyone knows how to pass a private key to generate token?
first thing is whether you had missed a dot in your code acccidentally or not so put that and the parameters for signWith() is not correct i.e. first parameter is key and second one is signature algorithm. try this:
val encodeKey = Base64.encode(privateKey.toByteArray(), android.util.Base64.DEFAULT)
val jwt = Jwts.builder().setIssuer("firebase-adminsdk-kywht#...")
.setSubject("firebase-adminsdk-kywht#...")
.setAudience("https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit")
.setExpiration(calendar.time) //a java.util.Date
.setIssuedAt(Date())
.setId(UUID.randomUUID().toString())
.signWith(encodeKey, SignatureAlgorithm.RS512) //changed here
.compact()
I'm totally lost as to how to do this. I want to be able to authenticate a user with their username and password only--so I have to use a customAuth from Firebase.
I created a server (node.js) that handles the generation of tokens (runs on Heroku):
var express = require('express')
var Firebase = require('firebase')
var app = express()
app.set('port', (process.env.PORT || 5000))
app.use(express.static(__dirname + '/public'))
app.listen(app.get('port'), function() {
console.log("Node app is running at localhost:" + app.get('port'))
})
var SECRET = "numbers would be here";
var tokenGenerator = new FirebaseTokenGenerator(SECRET);
var AUTH_TOKEN = tokenGenerator.createToken({
uid: "arbitrary",
data: "blahblahblah"});
console.log(AUTH_TOKEN);
var ref = new Firebase("null");
ref.authWithCustomToken(AUTH_TOKEN, function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Login Succeeded!", authData);
}
});
Now I have an Android app in which I want to authenticate a user. If I have something like,
Firebase mRef = new Firebase("myFirebaseUrl");
mRef.authWithCustomToken(String token, AuthResultHandler handler); //issue
I don't know how to get the token. Furthermore, I'm not sure I understand how it matters if the token is always the same.
You'll need to come up with a secure way to communicate the username and password from your Android client to the node.js server and to subsequently communicate the resulting token (or any error codes) back from the node.js server to the client.
While this is definitely possible (it's pretty much how Firebase email+password authentication works), it is definitely too broad a topic to cover in a StackOverflow answer. It's a project, rather than a question.
What you can consider is using Firebase email+password auth and then stubbing out the email domain. So if a user signs up with username Nxt3 and password, you simply append a dummy domain to the username and register them as Nxt3#dummydomain.com.
My desktop app used www.google.com/accounts/ClientLogin (which currently unavailable) to obtain authentification token, that i used to get android application info from unofficial market api (https://androidquery.appspot.com/api/market?app=(put the package name here))
Now Google want me to use OAuth authorization because ClientLogin deprecated and response 404 all time.
So question is - how can i get android application info by "appId" (just version code for example - "23") using OAuth 2.0 and Google Client Api Libraries for .NET?
And another question - how i can manually generate this request
POST "https://accounts.google.com/o/oauth2/token HTTP/1.1"
User-Agent: google-api-dotnet-client/1.9.3.19379 (gzip)
Content-Type: application/x-www-form-urlencoded
Host: accounts.google.com
Content-Length: 750
Connection: Keep-Alive
assertion=?
I can see in Fiddler how this request send from google lib? but it stores the response inside lib and i can't access to auth token:
{
"access_token" : "TOKEN_HERE",
"token_type" : "Bearer",
"expires_in" : 3600
}
???
I found solution for this problem.
Google Api provides one method to obtain apllication version code.
Firstly, you need to create a project in Google Developers Console, create credentials for Service Account with p12 key file.
And enable Google Play Developers Api.
In Google Play Developers Console you should link your app to this project.
After, you can write this code in eour desktop .NET appliation:
var serviceAccountEmail = "YOUR Service Account Email";
var certificate = new X509Certificate2(#"key.p12", "YOUR_CLIENT_SECRET", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { AndroidPublisherService.Scope.Androidpublisher }
}.FromCertificate(certificate));
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Edits Sample"
});
var apiEditBody = new AppEdit();
// packageName - your app id like com.myapp.test
var appEdit = service.Edits.Insert(apiEditBody, packageName)
.Execute();
var list = service.Edits.Apks.List(packageName, appEdit.Id)
.Execute()
.Apks;
var deletingEditResult = service.Edits.Delete(packageName, appEdit.Id).Execute();
var versionCode = list.Last().VersionCode.Value;
That's it.
Hope, this answer will help somebody =)
Similar to solution above which was very helpful to me, here is a solution that gets the latest production track version code of your app using the Google API's:
var path = "PATH_TO_JSON_KEY";
var credential = GoogleCredential.FromFile(path).CreateScoped(AndroidPublisherService.Scope.Androidpublisher);
var service = new AndroidPublisherService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "Production Version Checker" });
var appEdit = service.Edits.Insert(new AppEdit(), "com.package.name").Execute();
var listTracks = service.Edits.Tracks.List("com.package.name", appEdit.Id).Execute();
var productionTrack = listTracks.Tracks.FirstOrDefault(t => t.TrackValue == "production");
var latestProductionRelease = productionTrack.Releases.FirstOrDefault(r => r.Status == "completed");
var latestProductionVersionCode = latestProductionRelease.VersionCodes.FirstOrDefault();
var deletingEditResult = service.Edits.Delete("com.package.name", appEdit.Id).Execute();
I am receiving an "Audience not allowed" warning in my google developers console logs when trying to make an authenticated request via Google Cloud Endpoints from an Android app.
Looking through the Endpoints source code, that corresponds to:
aud = parsed_token.get('aud')
cid = parsed_token.get('azp')
if aud != cid and aud not in audiences:
logging.warning('Audience not allowed: %s', aud)
My calling code in the android app:
public static final String WEB_CLIENT_ID = "web-client-id.apps.googleusercontent.com";
public static final String AUDIENCE = "server:client_id:" + WEB_CLIENT_ID;
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(
mContext,
AUDIENCE
);
Grapi.Builder builder = new Grapi.Builder(HTTP_TRANSPORT,
JSON_FACTORY, credential);
Grapi service = builder.build()
Where "web-client-id" is the alpha numeric client id generated in google developers console. This service is used to make authenticated calls.
This is also the same WEB_CLIENT_ID that is passed to the api decorator in my backend python code:
WEB_CLIENT_ID = 'web-client-id.apps.googleusercontent.com'
ANDROID_CLIENT_ID = 'android-client-id.apps.googleusercontent.com'
ANDROID_AUDIENCE = WEB_CLIENT_ID
grapi_client_ids = [ANDROID_CLIENT_ID,
WEB_CLIENT_ID,
endpoints.API_EXPLORER_CLIENT_ID]
grapi_audiences = [ANDROID_AUDIENCE]
#endpoints.api(name='grapi', version='v1',
allowed_client_ids=grapi_client_ids, audiences=grapi_audiences,
scopes=[endpoints.EMAIL_SCOPE])
It looks like all of this is causing endpoints.get_current_user() to return None, and my authenticated call to fail.
When I initialized my web client id and android client id variables in the python backend, I used backslashes for line continuation to conform with PEP8 (80 character line length) ie.
WEB_CLIENT_ID = 'web-client-id'\
'.apps.googleusercontent.com'
ANDROID_CLIENT_ID = 'android-client-id'\
'.apps.googleusercontent.com'
I am not sure why this was not read correctly, but when I used line continuation inside parenthesis it worked fine.
WEB_CLIENT_ID = ('web-client-id'
'.apps.googleusercontent.com')
ANDROID_CLIENT_ID = ('android-client-id'
'.apps.googleusercontent.com')
I'm trying to access a Purchase Status API from my ASP.NET web server using Google APIs .NET Client Library which is a recommended way for using Purchase API v1.1. However, the Authorization page of this API suggests direct web requests to Google's OAuth2 pages instead of using the corresponding client libraries.
OK, I tried both methods with all variations I could imagine and both of them lead to "The remote server returned an error: (400) Bad Request.".
Now what I've done to get to my point. First I've made all steps 1-8 under the Creating an APIs Console project of the Authorization page. Next I generated a refresh token as described there. During refresh token generation I chose the same Google account as I used to publish my Android application (which is in published beta state now).
Next I've created a console C# application for test purposes in Visual Studio (may be console app is the problem?)
and tried to call the Purchase API using this code (found in some Google API examples):
private static void Main(string[] args)
{
var provider =
new WebServerClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = "91....751.apps.googleusercontent.com",
ClientSecret = "wRT0Kf_b....ow"
};
var auth = new OAuth2Authenticator<WebServerClient>(
provider, GetAuthorization);
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = APP_NAME
});
var request = service.Inapppurchases.Get(
PACKAGE_NAME, PRODUCT_ID, PURCHASE_TOKEN);
var purchaseState = request.Execute();
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
}
private static IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state =
new AuthorizationState(
new[] {"https://www.googleapis.com/auth/androidpublisher"})
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI"
};
// below is my redirect URI which I used to get a refresh token
// I tried with and without this statement
state.Callback = new Uri("https://XXXXX.com/oauth2callback/");
client.RefreshToken(state); // <-- Here we have (400) Bad request
return state;
}
Then I tried this code to get the access token (I found it here: Google Calendar API - Bad Request (400) Trying To Swap Code For Access Token):
public static string GetAccessToken()
{
var request = WebRequest.Create(
"https://accounts.google.com/o/oauth2/token");
request.Method = "POST";
var postData =
string.Format(
#"code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code",
// refresh token I got from browser
// also tried with Url encoded value
// 4%2FlWX1B3nU0_Yax....gAI
"4/lWX1B3nU0_Yax....gAI",
// ClientID from Google APIs Console
"919....1.apps.googleusercontent.com",
// Client secret from Google APIs Console
"wRT0Kf_bE....w",
// redirect URI from Google APIs Console
// also tried Url encoded value
// https%3A%2F%2FXXXXX.com%2Foauth2callback%2F
"https://XXXXX.com/oauth2callback/");
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
using (var dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
}
try
{
// request.GetResponse() --> (400) Bad request again!
using (var response = request.GetResponse())
{
using (var dataStream = response.GetResponseStream())
{
using (var reader = new StreamReader(dataStream))
{
var responseFromServer = reader.ReadToEnd();
var jsonResponse = JsonConvert.DeserializeObject<OAuth2Response>(responseFromServer);
return jsonResponse.access_token;
}
}
}
}
catch (Exception ex) { var x = ex; }
return null;
}
So, to sum up all my long story:
Is it possible at all to pass OAuth2 authorization using either of methods above from a C# Console Application (without user interaction)?
I've double checked the redirect URI (since I saw a lot of discussed troubles because of it here on stackoverflow) and other parameters like ClientID and ClientSecret. What else I could do wrong in the code above?
Do I need to URL encode a slash in the refresh token (I saw that the first method using client library does it)?
What is the recommended way of achieving my final goal (Purchase API access from ASP.NET web server)?
I'll try to answer your last question. If you access your own data account, you dont need to use client id in oAuth2. Let's use service account to access Google Play API.
Create a service account in Google Developer Console > Your project > APIs and auth > Credentials > Create a new key. You will download a p12 key.
Create a C# project. You can choose console application.
Install google play api library from Google.Apis.androidpublisher. Nuget. You can find other library for dotnet in Google APIs Client Library for .NET
Link google api project with your google play account in API access
Authenticate and try to query information. I'll try with listing all inapp item. You can just change to get purchase's status
String serviceAccountEmail = "your-mail-in-developer-console#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"physical-path-to-your-key\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://www.googleapis.com/auth/androidpublisher" }
}.FromCertificate(certificate));
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GooglePlay API Sample",
});
// try catch this function because if you input wrong params ( wrong token) google will return error.
var request = service.Inappproducts.List("your-package-name");
var purchaseState = request.Execute();
// var request = service.Purchases.Products.Get(
//"your-package-name", "your-inapp-item-id", "purchase-token"); get purchase'status
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
You should do the following in your
private static IAuthorizationState GetAuthorization(WebServerClient client) method:
private IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state = AuthState;
if (state != null)
{
return state;
}
state = new AuthorizationState()
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI",
Callback = new Uri(#"https://XXXXX.com/oauth2callback/")
};
client.RefreshToken(state);
// Store and return the credentials.
HttpContext.Current.Session["AUTH_STATE"] = _state = state;
return state;
}
Let me know if it works for you.
Be aware that we know that the whole OAuth2 flow is awkward today, and we are working to improve it.