Recently implemented Asp.net core 2.0 WEB Api. Works smashingly on my local dev environment. HOWEVER ... when i deploy to AZURE i find that my JWT Access Token does NOT contain the Issuer and Audience claims and therefore i get the 401 Unauthorized with : Bearer error="invalid_token", error_description="The audience is invalid". The JWT generated on my local machine has : (courtesy of jwt.io)
{
"http://schemas.xmlsoap.org/...": "Rory#gspin.com",
"sub": "Rory#gspin.com",
"given_name": "Rory",
"family_name": "McGilroy",
"email": "Rory#gspin.com",
"jti": "3875f83d-eb93-4d45-8507-795a0cb7e3e4",
"iat": 1533506381,
"rol": "api_access",
"id": "420990b2-4747-4c3c-ae0f-ccbbc4dfe521",
"nbf": 1533506381,
"exp": 1533513581,
"iss": "gspin.com",
"aud": "https://www.gspin.com"
}
But after deploying same application to AZURE APP Service my Access Token contains the following:
{
"http://schemas.xmlsoap.org/...": "billyttom#fido.com",
"sub": "billyttom#fido.com",
"given_name": "billy mark tom",
"family_name": "last",
"email": "billyttom#fido.com",
"jti": "0d34a03f-31ae-45aa-9ace-004d5916b430",
"iat": 1533498384,
"rol": "api_access",
"id": "5485d641-974b-4f60-ade6-35c048503701",
"nbf": 1533498383,
"exp": 1533505583
}
Missing iss and aud???
Any idea why these are being dropped when deployed to azure when they are defined and present in Token generated on local machine/Visual Studio env?
My Code is : public async Task<string> GenerateEncodedToken(string
userName, ClaimsIdentity identity, UserManager<GSIdentityUser> _userManager)
{
var user = await _userManager.FindByNameAsync(userName);
var userClaims = await _userManager.GetClaimsAsync(user);
var claims = new[]
{
new Claim(ClaimTypes.Name, userName),
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.GivenName, user.FirstName),
new Claim(JwtRegisteredClaimNames.FamilyName, user.LastName),
new Claim(JwtRegisteredClaimNames.Email, user.Email), /// same as username
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()), // the uniqueness claim is a GUID
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
identity.FindFirst(Helpers.Constants.Strings.JwtClaimIdentifiers.Rol),
identity.FindFirst(Helpers.Constants.Strings.JwtClaimIdentifiers.Id)
};
// Create the JWT security token and encode it.
var jwt = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claims,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
public ClaimsIdentity GenerateClaimsIdentity(string userName, string id)
{
return new ClaimsIdentity(new GenericIdentity(userName, "Token"), new[]
{
new Claim(Helpers.Constants.Strings.JwtClaimIdentifiers.Id, id),
new Claim(Helpers.Constants.Strings.JwtClaimIdentifiers.Rol, Helpers.Constants.Strings.JwtClaims.ApiAccess)
});
}
Also in ConfigureServices i have :
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = Configuration["JwtIssuerOptions:Issuer"];
options.Audience=Configuration["JwtIssuerOptions:Audience"];
options.SigningCredentials = new SigningCredentials(_signingKey,
SecurityAlgorithms.HmacSha256);
});
Related
In my cloud function, I need to create a customtoken and insert some custom claims. I do so like this:
let additionalClaims = {
'https://hasura.io/jwt/claims': {
'x-hasura-default-role': 'admin',
'x-hasura-allowed-roles': ['user', 'admin'],
}
}
admin.auth().createCustomToken(userId,additionalClaims).then(function (customToken) {
console.log(customToken);
response.end(JSON.stringify({
token: customToken
}))
})
.catch(function (error) {
console.log('Error creating custom token:', error);
});
The Claims get added, but they are put in a "claims" node, that looks like this:
{
"aud":
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iat": 1573160781,
"exp": 1573164381,
"iss": "postgrest-b4c8c#appspot.gserviceaccount.com",
"sub": "postgrest-b4c8c#appspot.gserviceaccount.com",
"uid": "mikeuserid",
"claims": {
'https://hasura.io/jwt/claims': {
'x-hasura-default-role': 'admin',
'x-hasura-allowed-roles': ['user', 'admin'],
}
}
}
}
However, the API I am calling is expecting them in the root, like this:
{
"aud":
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iat": 1573160781,
"exp": 1573164381,
"iss": "postgrest-b4c8c#appspot.gserviceaccount.com",
"sub": "postgrest-b4c8c#appspot.gserviceaccount.com",
"uid": "mikeuserid",
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "admin",
"x-hasura-allowed-roles": ["user", "admin"],
}
}
}
How can I add the claims so they are added to root, and not under "claims" ??
FYI, if I have an existing user, and use this syntax
admin.auth().setCustomUserClaims(customToken, additionalClaims),
It correctly adds them root.
You can't add claims to the top-level (root) of a Firebase custom token. You should also not pass custom tokens to other APIs. They should only be used to initiate a sign-in from a client device. The sign-in operation will exchange the custom token for an ID token with the custom claims at the root of the JWT.
When you add claims using setCustomUserClaims(), there are no custom tokens involved (usually). The user receives an ID token from Firebase Auth, with custom claims already set at the root.
I created a API that I try to protect with AAD.
It's already working for Accounts from my organization and Accounts from other organizations but not for personal Microsoft Accounts.
I already tryed different Endpoints but I think the common Endpoint should be the correct Endpoint if I want any Account to be able to sign in.
This is how my API Startup looks:
services.AddAuthentication(o =>
{
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.Authority = "https://login.microsoftonline.com/common";
o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
// Both App ID URI and client id are valid audiences in the access token
ValidAudiences = new List<string>
{
"APP ID",
},
ValidateIssuer = false,
};
});
And this is how I get the accesstoken at javascript:
var applicationConfig = { //Cloudlist API via TestApp
clientID: "APP ID",
authority: "https://login.microsoftonline.com/common",
graphScopes: ["https://hsde.onmicrosoft.com/APP ID/User"]
};
var myMSALObj = new Msal.UserAgentApplication(applicationConfig.clientID,
applicationConfig.authority, null, { storeAuthStateInCookie: true,
cacheLocation: "localStorage" });
myMSALObj.loginPopup(applicationConfig.graphScopes).then(function (idToken) {
myMSALObj.acquireTokenSilent(applicationConfig.graphScopes).then(function (accessToken) {
callAPI(accessToken);;
});
}, function (error) {
console.log(error);
});
When I sign in with a personal Microsoft Account and use the accessToken to call the API I get a 401 Unauthorized Error.
The response header says:
www-authenticate: Bearer error="invalid_token", error_description="The signature key was not found"
Is there anything I have to do differently when signing in with a personal Microsoft Account ?
First, get a token and try to decoded in jwt.io just to check if audience id is the same that you are using in you web api.
I am trying to use a custom auth token with firestore. I am using nodejs to generate the token with the following code.
const admin = require('firebase-admin');
const serviceAccount = require('./ServiceAccountKey.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
var uid = "some-uid";
var claim = {
control: true
};
admin.auth().createCustomToken(uid, true)
.then(function(customToken) {
console.log(customToken)
})
.catch(function(error) {
console.log("Error creating custom token:", error);
});
When I run it I get a token. I take that token and try it out using
https://firestore.googleapis.com/v1beta1/projects/example-project-5caa9/databases/(default)/documents/users with the headers
Authorization:Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsiY29udHJvbCI6dHJ1ZX0sInVpZCI6InNvbWUtdWlkIiwiaWF0IjoxNTI4MTQ0NzY3LCJleHAiOjE1MjgxNDgzNjcsImF1ZCI6Imh0dHBzOi8vaWRlbnRpdHl0b29sa2l0Lmdvb2dsZWFwaXMuY29tL2dvb2dsZS5pZGVudGl0eS5pZGVudGl0eXRvb2xraXQudjEuSWRlbnRpdHlUb29sa2l0IiwiaXNzIjoiZmlyZWJhc2UtYWRtaW5zZGsteG9jMDRAZXhhbXBsZS1wcm9qZWN0LTVjYWE5LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwic3ViIjoiZmlyZWJhc2UtYWRtaW5zZGsteG9jMDRAZXhhbXBsZS1wcm9qZWN0LTVjYWE5LmlhbS5nc2VydmljZWFjY291bnQuY29tIn0.Bjl6VY5CZKIpNyCayROWr_ZBSRmo11hiwtnx_cbbw2Ggk3J2x0Ml2OkpXhU-vAD6Q53fCZwGgXeCdxnsXw0lr55cJH3Q6J7gitzQoRnfJgUX9Dv1gbI90OWashxMmxtzPIpwgSnfBv61mkdv9ZVrF8o362mQBx_LUQzvGgVPEN9_9UNCH7peOS4KYr_YRMpCQVem0XMNh9WKlyBZuScjHpY6dZZhXqOHda0W9-MNAfvQ-D0pt-osq4ty-D_WYk6CjLNmxzvHoZeoIk1YShJM4Mpyec3lXFcCXNYG2c3_r2tskTB0LF7Fc7Bg5XuJwlrAzHrnRis6iZFCx8sqH1b-Zg
get the following JSON.
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
My rules are as follow
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
}
}
You can't directly access REST APIs with a custom token. You need to sign in using the custom token, and obtain an ID token. There are 2 ways to do this:
Sign in from a Firebase Client SDK
Use the Firebase Auth REST API to exchange the custom token to an ID token.
Then you can access the Firestore REST API with the resulting ID token:
https://firebase.google.com/docs/firestore/use-rest-api#working_with_firebase_id_tokens
I am trying to use service authentication mechanism for logging in. But getting an invalid request.I have created the JWT using node jsonwebtoken package.below is the error I am getting.
Copying the JWt creation logic
var jwt = require('jsonwebtoken');
var fs = require('fs');
//creating the payload for docusign
var payload = {
"iss": "cb91877d-1d55-48ae-88d7-fd215c4fe2ca",
"sub": "6cd4ea4e-3e68-4994-88b1-a321847cbf7e",
"iat": 1508868716,
"exp": 1508869916,
"aud": "account-d.docusign.com",
"scope": "signature",
"nbf":1508868716,
"name": "neeush"
}
var cert = fs.readFileSync('private.pem'); // get private key
var token = jwt.sign(payload, cert, { algorithm: 'RS256'});
Can you find any issues in the same.
enter image description here
var jwt = require('jsonwebtoken');
var fs = require('fs');
//creating the payload for docusign
var payload = {
"iss": "cb91877d-1d55-48ae-88d7-fd215c4fe2ca",
"sub": "6cd4ea4e-3e68-4994-88b1-a321847cbf7e",
"iat": 1509096042,
"exp": 1509099042,
"aud": "account-d.docusign.com",
"scope": "signature impersonation",
"name" :"name value" //not required
}
var cert = fs.readFileSync('private.pem'); // get private key
var token = jwt.sign(payload, cert, { algorithm: 'RS256'});
console.log(token)
console.log('---------------------------------------------------');
//printing the token to a file
fs.writeFile('jwttoken.txt', token, function (err) {
if (err)
return console.log(err);
});
When the this generated token is used able to get post correct.
Earlier the token was printed on th console and copied from there which was creating the problem.
#Larry K Thanks for the help with the scope claim.
Remove nbf and name claims.
Change scope claim to "signature impersonation"
See https://docs.docusign.com/esign/guide/authentication/oa2_jwt.html#creating-the-jwt-token
I'm currently working on a project where I have a Web API that uses the Graph API to create accounts in my Azure AD and put them in the correct group as well.
Next to that I have several API calls exposed that will be called by a native iOS app as well as an angularJS web app. The client has concocted some weird way of doing the authentication because he firmly believes his users to be utter morons.
The client is handing custom prepped iOS devices to certain people. The device has an Azure AD User(principal) and password. Once they are handed out, some process on the angularJS web app does this, the app will then call my token controller that looks like this:
public async Task<string> GetTokenForUserAsync(string userPrincipalName, string password)
{
var uri = string.Format("{0}/oauth2/token", AuthString);
using (var httpClient = new HttpClient
{
DefaultRequestHeaders = { Accept = { new MediaTypeWithQualityHeaderValue("application/json") } }
})
{
var values = new Dictionary<string, string>
{
{"resource", GraphUrl },
{"client_id", ClientId },
{"client_secret", ClientSecret },
{"grant_type", "password" },
{"username", userPrincipalName },
{"password", password },
{"scope", "openid" }
};
var content = new FormUrlEncodedContent(values);
var response = await httpClient.PostAsync(uri, content);
var responseContent = await response.Content.ReadAsStringAsync();
return responseContent;
}
The passed parameters are, not 100% exact, but very alike:
AuthString : "https://login.microsoftonline.com/myAzureAD.onmicrosoft.com"
GraphUrl : "https://graph.windows.net"
ClientId : My Web API ClientId (a guid)
ClientSecret : My Web API Client Secret (2year valid access key)
So this call actually provides me with an access_token. The problem that I'm having is that the tokens I get are never authorized. I've tried several Startup setups, but none are working.
Currently I've got the following code in my Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:ClientId"]
}
});
//app.UseWindowsAzureActiveDirectoryBearerAuthentication(
// new WindowsAzureActiveDirectoryBearerAuthenticationOptions
// {
// TokenValidationParameters = new TokenValidationParameters
// {
// ValidAudience = ConfigurationManager.AppSettings["ida:ClientId"]
// },
// Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
// });
}
It's the first time that I'm working with Azure and Active Directory. So I'm probable doing something really stupid. At this moment in time I don't care much about styling and 'the right way'. I just want this thing to work :,/
Hope I described my problem correctly and documented my question accordingly. If you have any questions, please don't hesitate to ask!
Thanks a bunch in advance
I dont think you can get accesstoken from var values = new Dictionary
{
{"resource", GraphUrl },
{"client_id", ClientId },
{"client_secret", ClientSecret },
{"grant_type", "password" },
{"username", userPrincipalName },
{"password", password },
{"scope", "openid" }
};
so you could try other methods:
AuthenticationContext aContext =
new AuthenticationContext("https://login.microsoftonline.com/tenantid");
AuthenticationResult aResult =
aContext.AcquireToken("https://graph.windows.net",
"1950a258-227b-4e31-a9cf-717495945fc2",
new UserCredential(UserId, PasswordId));
string result = string.Empty;
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", aResult.AccessToken);
HttpResponseMessage response =
httpClient.GetAsync("https://graph.windows.net/tenantid/users/userid?api-version=1.6").Result;
if (response.IsSuccessStatusCode)
{
result = response.Content.ReadAsStringAsync().Result;
}
Console.WriteLine(result);
Console.ReadLine();