I'm trying to Verify an Id Token in C#
I'm Creating the app like this:
AppOptions appOptions = new AppOptions()
{
Credential = GoogleCredential.FromFile(#"path/to/Credential.json"),
ServiceAccountId = "serviceAccId",
ProjectId = "ProjectId",
};
var MyApp = FirebaseApp.Create(appOptions);
The error message i get is: ID token has incorrect audience (aud) claim.
Any Ideas on what it could be? Thanks!
I added a few things to the code... the problem i get is in the last step, when i try to signIn with custom token.
It gives me an error stating that the reason was a MissingIdentifier.
using (var customToken = FirebaseAdmin.Auth.FirebaseAuth.DefaultInstance.CreateCustomTokenAsync(authentication.FirebaseUser.LocalId))
{
string token = customToken.Result;
using (FirebaseAuthProvider auth = new FirebaseAuthProvider(new FirebaseConfig(FireBaseAppKey)))
{
using (test = auth.SignInWithCustomTokenAsync(token))
{
test.Wait();
}
}
customToken.Wait();
}
This is the message i get:
Exception occured while authenticating.
Url: https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key={0}
Request Data:
{
"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJVaWQiOiIyM2E1ZGM0Ny03NDNhLTQzNDUtODc5Mi1lMDY5NjhkNDZjNGIiLCJpc3MiOiJmaXJlYmFzZS1hZG1pbnNkay0xb2ZxNEBhdXRodGVzdHByb2plY3QtYmVlMDkuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzdWIiOiJmaXJlYmFzZS1hZG1pbnNkay0xb2ZxNEBhdXRodGVzdHByb2plY3QtYmVlMDkuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJhdWQiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsImV4cCI6MTU4NDU1NTAzNiwiaWF0IjoxNTg0NTUxNDM2fQ.nwvRalOpMs9LYIAFoFZ53Yu72kar9MNpO8gHBGZaMQcdx0ms7OIs0cYEsXUDYy0A_rNfOK03pIWc1y_w2rtIbl_Rg7oHY2u8YublHGe
-n6w9PjQpkONU3YEWHW9qnewhYPFqiLw94j8qEM9V3Bc0FCtspyv8i7Ra9-r2Gz9p88kvUHcIV8_qF9dN_4kNVNiVVHOIhFDQgDOnwUSobmp6aMVnsB9xRwv2_oiWc19s4HNXcNif12d7HHdeRauWVRoTYYvMjrgJTRUsGcB2YFZR8QhH7_0Fmn8bfbiJWP2maTXayL4sY2sIaEyJZDIaBDHkU8l_j_1KxBR7_FTv2Q5_DA\",
"returnSecureToken":true
}
Response: {
"error": {
"code": 400,
"message": "MISSING_IDENTIFIER",
"errors": [
{"message": "MISSING_IDENTIFIER\",
"domain": "global",
"reason": "invalid"
}
]
}
}
Reason: MissingIdentifier"}
This typically means that the ID token is for a different project than what you have the credentials file for. I recommend downloading a fresh credentials file from the Firebase/Cloud console for the project, and trying again.
Related
Because Microsoft ends the support for Basic Authentication access for IMAP in Office 365 I try to update our application to use OAuth 2.0. We use MailKit in a MVC .Net web-application to access an IMAP mailbox, but I get an error saying Authentication failed. However, as a test, I can get it to work in a c# console-application.
The strange thing is:
If I copy the access-token I acquired using the console-application and use it in my web-application I can successfully authenticate and read emails. So that part works.
The authentication itself seems to be successful in the web-application. Our webapp redirects to the Microsoft login-page, MFA works, I see successful audits in Azure A/D and I do get a token in the callback. However, this token gives the Authentication failed error by Mailkit.
In Azure A/D I see some of these errors between the successful audits, but I'm not sure whether they are related or not: Error AADSTS16000 SelectUserAccount - This is an interrupt thrown by Azure AD, which results in UI that allows the user to select from among multiple valid SSO sessions. This error is fairly common and may be returned to the application if prompt=none is specified.
I already verified that the scope for which I acquire a token is the same for both console and web.
The main difference is that I use pca.AcquireTokenInteractive(scopes) in the console application to acquire the token, but I use a webclient call with a call-back in the MVC-controller.
Here is my code (MVC):
public ActionResult Index()
{
string clientID = "[client-id here]";
string clientSecret = "[client-secret here]";
string redirectUri = "[redirectUri here]";
AuthorizationServerDescription server = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize"),
TokenEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/token"),
ProtocolVersion = ProtocolVersion.V20,
};
List<string> scopes = new List<string>
{
"email",
"offline_access",
"https://outlook.office365.com/IMAP.AccessAsUser.All"
};
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
OutgoingWebResponse response = consumer.PrepareRequestUserAuthorization(
scopes, new Uri(redirectUri));
return response.AsActionResultMvc5();
}
public async Task<ActionResult> Authorized(string code, string state, string session_state)
{
List<string> scopes = new List<string>
{
"IMAP.AccessAsUser.All",
"User.Read",
"offline_access"
};
HttpClient httpClient = new HttpClient();
var values = new Dictionary<string, string>
{
{ "Host", "https://login.microsoftonline.com" },
{ "Content-Type", "application/x-www-form-urlencoded" },
{ "client_id", "[client-id here]" },
{ "scope", string.Join(" ",scopes) },
{ "code", code },
{ "redirect_uri", [redirectUri here] },
{ "grant_type", "authorization_code" },
{ "client_secret", "[client-secret here]" },
{ "state", state },
};
var content = new FormUrlEncodedContent(values);
var response = await httpClient.PostAsync("https://login.microsoftonline.com/organizations/oauth2/v2.0/token", content);
var jsonString = await response.Content.ReadAsStringAsync();
var oathToken = JsonConvert.DeserializeObject<OathToken>(jsonString);
var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);
var stringBuilder = new StringBuilder();
using (var client = new ImapClient())
{
try
{
await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.Auto);
await client.AuthenticateAsync(oauth2);
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
for (int i = 0; i < inbox.Count; i++)
{
var message = inbox.GetMessage(i);
stringBuilder.AppendLine($"Subject: {message.Subject}");
}
await client.DisconnectAsync(true);
return Content(stringBuilder.ToString());
}
catch (Exception e)
{
return Content(e.Message);
}
}
}
The error Authentication failed occurs at the line
await client.AuthenticateAsync(oauth2);
The problem was the scope "email".
We had to remove that. Exactly why, I don't know. It was no problem when used in the console app. Maybe it had to do with the fact we used pca.AcquireTokenInteractive(scopes) in that.
The following is the request I'm using for the PATCH request for updating a user's password.
var token = TokenHelper.GetToken().AccessToken;
var client = new RestClient("https://graph.microsoft.com/v1.0/users/" + person.UserPrincipalName);
client.Timeout = -1;
var request = new RestRequest(Method.PATCH);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer " + token);
request.AddParameter("application/json", "{\n\"passwordProfile\": {\n \"password\": \"" + person.NewPassword + "\"\n}\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
If I type a complex password I get:
{
"error": {
"code": "Request_BadRequest",
"message": "One or more properties contains invalid values.",
"innerError": {
"request-id": "5d97b465-7b27-4328-b0d9-4e9112f2257e",
"date": "2020-01-03T16:57:35"
}
}
}
If I type a simple password I get:
{
"error": {
"code": "Request_BadRequest",
"message": "The specified password does not comply with password complexity requirements. Please provide a different password.",
"innerError": {
"request-id": "986fd0da-90d4-45c7-ba74-1ba2bec61956",
"date": "2020-01-03T17:05:15"
}
}
}
If I type no password my response is a 204 No Content (success) and it is working fine if I update other fields(i.e. mobileNumber).
In order to change a user's password, you need to authenticate using either the Authorization Code or Implicit OAuth grant. In addition, you need to request the delegated scope Directory.AccessAsUser.All. From the documentation:
When updating the passwordProfile property, the following permission is required: Directory.AccessAsUser.All.
You should also set forceChangePasswordNextSignIn to true.
We've been using the same process for creating a google calendar invite for a year now, and if the the calendar invite and id already exists, it will create a new one.
This code is fairly static so I am wondering if something happened on google's API that might have changed?
Here is the error returned:
{
"ExceptionDetail": {
"HelpLink": null,
"InnerException": null,
"Message": "Google.Apis.Requests.RequestError The requested identifier already exists. [409] Errors [Message[The requested identifier already exists.] Location[ - ] Reason[duplicate] Domain[global]]",
"Type": "Google.GoogleApiException"
},
"ExceptionType": "Google.GoogleApiException",
"Message": "Google.Apis.Requests.RequestError The requested identifier already exists. [409] Errors [Message[The requested identifier already exists.] Location[ - ] Reason[duplicate] Domain[global]]",
And here is the snippet of code that is creating/updating the invite
service = Authentication.AuthenticateOauth();
//Google calendar ID for calendar request.
EventsResource.ListRequest checkIfMade = service.Events.List(newCalendar.calendarID);
Events events = checkIfMade.Execute();
var hasBeenMade = false;
foreach (var eventItem in events.Items)
{
if (eventItem.Id == convertedID)
{
hasBeenMade = true;
break;
}
}
if (hasBeenMade)
{
EventsResource.UpdateRequest singleEventRequest = new EventsResource.UpdateRequest(service, myEvent, newCalendar.calendarID, myEvent.Id);
singleEventRequest.SendNotifications = true;
singleEventRequest.Execute();
// Debug.Print(eventLink.HtmlLink);
}
else
{
EventsResource.InsertRequest singleEventRequest = new EventsResource.InsertRequest(service, myEvent, newCalendar.calendarID);
singleEventRequest.SendNotifications = true;
singleEventRequest.Execute();
//Debug.Print(eventLink.HtmlLink);
}
}
I am using Azure Data factory HTTP connector as a linked service to read data from the REST endpoint using basic authentication.
{
"name": "LS_HTTP",
"properties": {
"hubName": "Hub name",
"type": "Http",
"typeProperties": {
"url": "http://*****.azurewebsites.net",
"authenticationType": "Basic",
"gatewayName": "",
"encryptedCredential": "",
"username": "test",
"password": "**********",
"enableServerCertificateValidation": true
}
}
}
Following code snippet is written to fetch the username and password from the headerin my web API
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic"))
{
//Extract credentials
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
if (username == "test" && password == "****")
{
await _next.Invoke(context);
}
else
{
context.Response.StatusCode = 401; //Unauthorized
return;
}
}
else
{
// no authorization header
context.Response.StatusCode = 401; //Unauthorized
return;
}
When I run Azure data factory pipeline with this setup, I am not able to get username and password from the request header in the web api, basically Authorization header itself is null.
Help me to fetch the username and password passed from my ADF connector service in my web API.
Looking at your definition, you're working with Data Factory v1. I see you configure some properties that are not required for basic authentication.
encryptedCredential, is not required. The documentation states:
Description: Encrypted credential to access the HTTP endpoint. Auto-generated when you configure the authentication information in copy wizard or the ClickOnce popup dialog.
Required: No. Apply only when copying data from an on-premises HTTP server.
gatewayName, is not required since you're not using an on-premises HTTP server
Description: Name of the Data Management Gateway to connect to an on-premises HTTP source.
enableServerCertificateValidation, already defaults to true
Documentation gives this basic example:
{
"name": "HttpLinkedService",
"properties":
{
"type": "Http",
"typeProperties":
{
"authenticationType": "basic",
"url" : "https://en.wikipedia.org/wiki/",
"userName": "user name",
"password": "password"
}
}
}
Short version
Logged in as a Facebook user, I use my oAuth token to assume an IAM role on AWS. It returns what looks to be valid credentials, e.g. there is an AccessKeyId, SecretAccessKey that are similar length to our master keys.
When I try to use these credentials to access a DynamoDB table, I get one of two exceptions:
"The remote server returned an error: (400) Bad Request."; or
"The security token included in the request is invalid.".
I'm using the AWS C# SDK version 1.5.25.0
Long version
As I said above, I'm trying to access a DynamoDB table on AWS using credentials supplied by AmazonSecurityTokenServiceClient authorized by Facebook Identity as described in this AWS guide.
The policy for the IAM role that I've created is:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:BatchGetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem"
],
"Sid": "Stmt1372350145000",
"Resource": [
"*"
],
"Effect": "Allow"
}
]
}
How I get the credentials:
The user logs in with Facebook using oAuth.
Using the access token, I assume the IAM role using a AmazonSecurityTokenServiceClient.AssumeRoleWithWebIdentity with a request.
This returns what looks like to be valid credentials, e.g. a AccessKeyId, SecretAccessKey that are similar length to our master keys.
using (var tokenServiceClient = new AmazonSecurityTokenServiceClient(RegionEndpoint.USEast1))
{
var request = new AssumeRoleWithWebIdentityRequest
{
DurationSeconds = (int)TimeSpan.FromHours(1).TotalSeconds,
ProviderId = "graph.facebook.com",
RoleArn = "arn:aws:iam::193557284355:role/Facebook-OAuth",
RoleSessionName = result.id,
WebIdentityToken = FBClient.AccessToken
};
var response = tokenServiceClient.AssumeRoleWithWebIdentity(request);
AWSAssumedRoleUser = response.AssumeRoleWithWebIdentityResult.AssumedRoleUser;
AWSCredentials = response.AssumeRoleWithWebIdentityResult.Credentials;
}
How I use these credentials:
Using the returned credentials, I then try to access a AWS DynamoDB resource.
using (var client = new AmazonDynamoDBClient(AWSCredentials.AccessKeyId, AWSCredentials.SecretAccessKey, AWSCredentials.SessionToken, RegionEndpoint.USEast1))
{
var context = new DynamoDBContext(client);
var data = context.Scan<SomeData>();
}
This returns "The remote server returned an error: (400) Bad Request." when trying to Scan the table.
This is where the variation in the exception message is; if I omit the AWSCredentials.SessionToken from the above AmazonDynamoDBClient
using (var client = new AmazonDynamoDBClient(AWSCredentials.AccessKeyId, AWSCredentials.SecretAccessKey, RegionEndpoint.USEast1))
{
var context = new DynamoDBContext(client);
var data = context.Scan<SomeData>();
}
This returns "The security token included in the request is invalid." when trying to Scan the table.
Question
At this point I cannot tell what is wrong, are the credentials invalid or that I'm not passing everything through that is needed to AWS.
Can anyone offer any insight to what is wrong or how I could debug this further?
I cross-posted my question to the AWS forums and received an answer from an Amazon engineer.
https://forums.aws.amazon.com/message.jspa?messageID=465057
DynamoDBContext object invokes DescribeTable on the target table (and caches this data, so for optimal performance you would want to keep the context object around for as long as possible, so this call is only done once per target table). Modify your policy as follows:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:BatchGetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem",
"dynamodb:DescribeTable"
],
"Sid": "Stmt1372350145000",
"Resource": [
"*"
],
"Effect": "Allow"
}
]
}