JS Report Server Console error: Authorization server has no "username" field in its response, token assumed as invalid - identityserver4

My js report server with client ui applications was working with username filed by adding one claim e.g. Claim('username', 'user name value') in identityserver.
4 version 1.1. B ut recently client has upgraded the identityserver 4 version to 2.2.0 and netcoreapp2.1 .
Now the client ui applications get "Unauthorized" error. When I run the application locally I see one error in jsreport server console:
error: Authorization server has no "username" field in its response, token assumed as invalid.
I have tried to find solution in the sample:
https://github.com/bjrmatos/jsreport-with-authorization-server-sample but they have not upgraded the sample for latest .net core and identity server yet, I see the node js sample link "https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/NodeJsApi" does not exists. so I am unable to solve the issue. Can anyone help me please on this?
Thanks in advance.

I have found solution to solve this error by adding a claim type with user name and declare that in api resource scope:
new ApiResource
{
Name="jsreportscope",
DisplayName = "JavaScript based reporting platform",
Scopes=
{
new Scope()
{
Name="jsreportscope",
UserClaims = new List<string>(){"username" }
}
},
UserClaims = new List<string>(){"username"},
ApiSecrets = { new Secret("yoursecret".Sha256()) },
}
But now it goes to the previous problem that I fixed by adding a claim type matching the value of username field value with identity server 1.1 version but now we have upgraded the identity server version to 2.1 and again getting the error. The it was able to authorize any user of identity server for report access. Here is the jsreport.config.js code I am using:
{
"store": { "provider": "fs" },
"httpPort": 5004,
"allowLocalFilesAccess": true,
"extensions": {
"authentication": {
"cookieSession": {
"secret": "<your strong secret>"
},
"admin": {
"username": "IdentityServer4User#domain.com",
"password": "Password"
},
"authorizationServer": {
"tokenValidation": {
"endpoint": "http://localhost:5000/connect/introspect",
"usernameField": "username",
"activeField": "active",
"scope": {
"valid": ["jsreportscope"]
},
"auth": {
"type": "basic",
"basic": {
"clientId": "jsreport",
"clientSecret": "yoursecret"
}
}
}
},
"enabled": true
},
"authorization": {
"enabled": true
},
"sample-template": {
"createSamples": true
}
}
}
But now I can login and access reports from report server if login by only the user IdentityServer4User#domain.com any other users is getting unauthorized error. In the report server console the error is shown like:
error: username "OtherIdentityServer4User#domain.com" returned from authorization server is not a jsreport user. I don not want to add all the identity server users to js report server.

Related

Azure B2C token validation in API returns 401 Unauthorized due to invalid signature

I am trying to set up a web app and web API using Azure B2C as the auth mechanism, but apparently failing miserably. My web app and API are both registered in Azure, the return URIs set properly, a scope created for the API which is then granted to the app, an app client secret created, and I have tested this locally WITH IT WORKING, but now that I've published my API to my live Azure App Service, it goes wrong for some reason.
Locally, I have run my web API and web app separately, signed into the web app and then obtained an access token, called the API (running locally) and all works well. However, now the API is published and running live (i.e. https://api.myapp.net), I am running the web app locally, calling the live API and every single time I request an access token then send it to the API I get a 401 Unauthorized because apparently the signature is invalid. WHY?!?!?!?
Below are slightly redacted copies of my startup and app settings files. Note I also have Azure Front Door set up to redirect "login.myapp.net" to "myapp.b2clogin.com", this has been tested with both the user flows on the Azure dashboard and with my own app running locally and all is fine.
Here is my web app's Startup.cs file:
services.AddRazorPages();
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection(Constants.AzureAdB2C))
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { "https://myapp.net/api/query" })
.AddInMemoryTokenCaches();
services
.AddControllersWithViews()
.AddMicrosoftIdentityUI();
services
.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
services.AddAuthorization(authorizationOptions =>
{
authorizationOptions.AddPolicy(
App.Policies.CanManageUsers,
App.Policies.CanManageUsersPolicy());
});
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.ResponseType = "code";
options.SaveTokens = true;
}).AddInMemoryTokenCaches();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<AppData>();
services.AddScoped<IAuthTokensService, AuthTokensService>();
services.AddOptions();
My web app's appsettings.json file:
"AzureAdB2C": {
"Instance": "https://login.myapp.net",
"ClientId": "my-web-app-client-id",
"CallbackPath": "/signin-oidc",
"Domain": "my-b2c-tenant-id",
"SignedOutCallbackPath": "/signout/B2C_1_SignUpIn",
"SignUpSignInPolicyId": "B2C_1_SignUpIn",
"ResetPasswordPolicyId": "B2C_1_PasswordReset",
"EditProfilePolicyId": "B2C_1_ProfileEdit",
"ClientSecret": "my-client-secret"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
Calling for an access token in my web app:
private readonly ITokenAcquisition _tokenAcquisition;
public AuthTokensService(ITokenAcquisition tokenAcquisition)
{
_tokenAcquisition = tokenAcquisition;
}
/// <inheritdoc />
public async Task<string> GetToken()
{
return await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { "https://myapp.net/api/query" });
}
My web API's Startup.cs file:
if (Configuration.GetConnectionString("SQLConnection") == null)
{
throw new InvalidOperationException("ConfigureServices: Connection string 'SQLConnection' returned null.");
}
services.AddDbContext<MyAppDbContext>(
option => option.UseSqlServer(Configuration.GetConnectionString("SQLConnection")));
services.AddMicrosoftIdentityWebApiAuthentication(Configuration, Constants.AzureAdB2C);
services.AddControllers();
services.AddScoped<IMyRepository, MyRepository>();
And my web API's appsettings.json file:
"AzureAdB2C": {
"Instance": "https://login.myapp.net",
"ClientId": "my-web-api-client-id",
"Domain": "my-b2c-tenant-id",
"SignUpSignInPolicyId": "B2C_1_SignUpIn",
"SignedOutCallbackPath": "/signout/B2C_1_SignUpIn"
},
"ConnectionStrings": {
"SQLConnection": "Server=(localdb)\\mssqllocaldb;Database=MyAppDebug;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
Can anybody advise on why this is? The only sensible answer I've seen anywhere online is that Blazor uses a v1.0 endpoint to obtain public keys whereas v2.0 is required for the API (see here) and I can see this happening with my tokens, but when I followed the fix given on the Microsoft documentation, I then get an exception thrown on startup of my web app IDX20807: Unable to retrieve document from: 'System.String'.

Azure AD: Create user with extra identities

I'm having trouble creating a user in an Azure Active Directory with a custom identity. The body of my request looks like this:
{
"passwordProfile": {
"password": "password-value"
},
"accountEnabled": true,
"displayName": "FIRSTNAME LASTNAME",
"passwordPolicies": "DisablePasswordExpiration",
"creationType": "LocalAccount",
"identities": [
{
"issuerAssignedId": "avalid#email.com",
"signInType": "emailAddress",
"issuer": "my_tenant.onmicrosoft.com"
}
]
}
I've also tried a version of the request where I specify mailNickname and userPrincipalName. In every case, the creation fails with the error:
{
"error": {
"innerError": {
"date": "2020-02-20T17:23:48",
"request-id": "c5a7c8da-35bd-4ae2-9ae8-6714b672f035"
},
"message": "One or more properties contains invalid values.",
"code": "Request_BadRequest"
}
}
There's a code snippet in the C# docs that suggests this should be possible.
What am I missing?
Microsoft Graph allows you to manage user accounts in your Azure AD B2C directory by providing create, read, update, and delete methods in the Microsoft Graph API. You can migrate an existing user store to an Azure AD B2C tenant and perform other user account management operations by calling the Microsoft Graph API.
If you try to use this Azure AD Graph API request for a normal Azure AD tenant, it will get the same error massage as yours.
So, ensure the tenant you're trying to query is a B2C tenant.
Try to use the global admin of the B2C tenant (e.g. username#b2ctenant.onmicrosoft.com) to obtain a token. Then use the token in the head to use the API :
Request:
POST https://graph.windows.net/myorganization/users?api-version=1.6
Body Content-Type: application/json:
{
"passwordProfile": {
"password": "password-value"
},
"accountEnabled": true,
"displayName": "FIRSTNAME LASTNAME",
"mailNickname": "mspcai",
"passwordPolicies": "DisablePasswordExpiration",
"creationType": "LocalAccount",
"identities": [
{
"issuerAssignedId": "avalid#email.com",
"signInType": "emailAddress",
"issuer": "my_tenant.onmicrosoft.com"
}
]
}
Looks like you are referring to a beta snippet, please try the following endpoint:
https://graph.microsoft.com/beta/users

Identity Server 4 Silent Renew ErrorResponse: login_required

I have cloned the repo from the redux-oidc-example and it works for the most part but after a few hours it gives the following error:
Action payload: ErrorResponse: login_required
at new e (oidc-client.min.js:1)
at t [as _processSigninParams] (oidc-client.min.js:1)
at t [as validateSigninResponse] (oidc-client.min.js:1)
at oidc-client.min.js:1
UserManager.js looks like this:
const userManagerConfig = {
client_id: 'js.dev',
client_secret: 'secret',
redirect_uri: `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}/callback`,
response_type: 'id_token token',
scope: 'openid email profile role offline_access',
authority: 'http://localhost:8080',
silent_redirect_uri: `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}/silent_renew.html`,
automaticSilentRenew: true,
filterProtocolClaims: true,
loadUserInfo: true
};
and my identity server config:
{
"Enabled": true,
"ClientId": "js.dev",
"ClientName": "Javascript Client",
"ClientSecrets": [ { "Value": "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=" } ],
"AllowedGrantTypes": [ "implicit", "authorization_code" ],
"AllowedScopes": [ "openid", "email", "profile", "role", "offline_access" ],
"AllowOfflineAccess": true,
"AllowAccessTokensViaBrowser":true,
"RedirectUris": [
"http://localhost:8081/callback",
"http://localhost:8081/silent_renew.html"
],
"PostLogoutRedirectUris": [
"http://localhost:8081"
],
"AccessTokenLifetime": 900,
"RequireConsent": false
}
I noticed that prior to error last valid response had one cookie response(idsrv.session) with empty value with the expiry date set to the previous year:
I believe this to be the root cause of the issue, I searched it on related Github repo and tried to add the Cookie.SameSite to none but it didn't help:
services.AddAuthentication()
.AddSaml(Configuration,externalProviders.UseSaml)
.AddCookie(options => {
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.Cookie.SameSite = SameSiteMode.None;
});
Any idea!
This is likely due to your IDP session expiring - if you call the authorize endpoint with prompt=none but it's unable to satisfy that request because no valid session exists (i.e. authentication cookie does not exist or has expired) then it will return error=login_required.
If this occurs then the correct course of action is to do an interactive (i.e. prompt=login) sign in request in the top level browser window.
After searching the Identity Server 4 repo, I made the following changes to my code:
services.AddIdentityServer(options=>
{
options.Authentication.CookieLifetime = TimeSpan.FromDays(30);
options.Authentication.CookieSlidingExpiration = true;
})
.AddProfileService<ProfileService>()
.AddSigningCertificate(Configuration)
.AddInMemoryClients(Configuration.GetSection("IdentityServer:Clients"))
.AddInMemoryIdentityResources(Resources.GetIdentityResources());
It started working afterward, but you would have to login again after you close the browser or reopen a new tab I guess it's because of the sessionStorage.
When the session expires the signin-callback is being called by STS having a query parameter called 'error' with the value 'login_required'.
In the signin-callback, before completing sign-in, you can check for this query parameter and if it's found you can sign-out also from your web client.
I had the same issue and tried the proposed above, but for me, it actually was SameSiteMode not set correctly on IdentityServer Cookies. It caused Callback error: ErrorResponse: login_required right after login and after N attempts user was logged out.
This helped me https://github.com/IdentityServer/IdentityServer4/blob/main/src/IdentityServer4/host/Extensions/SameSiteHandlingExtensions.cs
What they do is based on this article https://devblogs.microsoft.com/dotnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
Hope this is useful.
Update.
I had another issue related to this when the user was logged out after re-opening a browser (especially on Android Chrome). login_required error was shown. I noticed that session cookie Expires/Max-Age was set to Session and not some future date. Probably because of that check session iframe (with src={identity server url}/connect/checksession) failed as Identity Server thought there was no session as cookie expired.
I tried setting cookie lifetime via options, but it didn't work as expected for some reason. Lifetime was always 14 days:
services.AddIdentityServer(options=>
options.Authentication.CookieLifetime = TimeSpan.FromDays(30);
options.Authentication.CookieSlidingExpiration = true;
})
Then I tried this and it worked for me:
services.ConfigureApplicationCookie(options => {
options.ExpireTimeSpan = sessionCookieLifetime;
options.SlidingExpiration = true;
})

Unable to create Power Bi Datasource using API

I'm trying to create Power Bi data source following Microsoft provided documentation. But getting Bad Request DMTS_InvalidConnectionDetailsError error. Here is my post data sample.
{
"dataSourceType": "SQL",
"connectionDetails": "{\"Server\":\"MySqlServer\",\"Database\":\"MySqlDatabase\"}",
"datasourceName": "New Datasource",
"credentialDetails": {
"credentialType": "Basic",
"credentials": "{\"credentialData\":[{\"name\":\"username\", \"value\":\"MyUsername\"},{\"name\":\"password\", \"value\":\"MyPassword\"}]}",
"encryptedConnection": "Encrypted",
"encryptionAlgorithm": "RSA-OAEP",
"privacyLevel": "None"
}
}
And here is the response json.
{
"error":
{
"code": "DMTS_InvalidConnectionDetailsError",
"pbi.error": {
"code": "DMTS_InvalidConnectionDetailsError",
"parameters": {},
"details": [],
"exceptionCulprit": 1
}
}
}
Anybody can help please?
The credentials from credential details need to be encrypted using the RSA algorithm before passing it to the API. You can use the algorithm from https://learn.microsoft.com/en-us/power-bi/developer/encrypt-credentials. Also, the name of the server needs to have double the amount of "\" characters if it has any.

Problems with Azure application manifest trying to authenticate with office-js-helpers in an Outlook web add-in

I'm using office-js-helpers in order to get an OAuth token in my Outlook web add-in so I can use it for OAuthCredentials with the EWS Managed API (code for that is in an Azure App Service using the ASP.NET Web API).
I have configured my app's application registration in my test Office 365 tenant (e.g. mytenant.onmicrosoft.com, which is NOT the same Azure subscription hosting the web app - if that matters) as a Native app with oauth2AllowImplicitFlow set to true. I used a Native app type instead of a Web/API app to bypass an unexpected error indicating my app requires admin consent - even though no application permissions were requested - but that's another story (perhaps I must use Native anyway - not 100% sure).
I made sure that the Redirect URI (aka reply URL) in the app registration points to the same page as the Outlook add-in (e.g. https://mywebapp.azurewebsites.net/MessageRead.html).
Here is my app manifest:
{
"appId": "a11aaa11-1a5c-484a-b1d6-86c298e8f250",
"appRoles": [],
"availableToOtherTenants": true,
"displayName": "My App",
"errorUrl": null,
"groupMembershipClaims": null,
"optionalClaims": null,
"acceptMappedClaims": null,
"homepage": "https://myapp.azurewebsites.net/MessageRead.html",
"identifierUris": [],
"keyCredentials": [],
"knownClientApplications": [],
"logoutUrl": null,
"oauth2AllowImplicitFlow": true,
"oauth2AllowUrlPathMatching": false,
"oauth2Permissions": [],
"oauth2RequiredPostResponse": false,
"objectId": "a11aaa11-99a1-4044-a950-937b484deb8e",
"passwordCredentials": [],
"publicClient": true,
"supportsConvergence": null,
"replyUrls": [
"https://myapp.azurewebsites.net/MessageRead.html"
],
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
}
]
},
{
"resourceAppId": "00000002-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6",
"type": "Scope"
},
{
"id": "a42657d6-7f20-40e3-b6f0-cee03008a62a",
"type": "Scope"
}
]
},
{
"resourceAppId": "00000002-0000-0ff1-ce00-000000000000",
"resourceAccess": [
{
"id": "2e83d72d-8895-4b66-9eea-abb43449ab8b",
"type": "Scope"
},
{
"id": "ab4f2b77-0b06-4fc1-a9de-02113fc2ab7c",
"type": "Scope"
},
{
"id": "5eb43c10-865a-4259-960a-83946678f8dd",
"type": "Scope"
},
{
"id": "3b5f3d61-589b-4a3c-a359-5dd4b5ee5bd5",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null
}
I also made sure to add the authority URLs to my add-in's manifest:
<AppDomains>
<AppDomain>https://login.windows.net</AppDomain>
<AppDomain>https://login.microsoftonline.com</AppDomain>
</AppDomains>
This is the code I'm using in the add-in for the authentication with office-js-helpers:
// The Office initialize function must be run each time a new page is loaded.
Office.initialize = function(reason) {
$(document).ready(function () {
// Determine if we are running inside of an authentication dialog
// If so then just terminate the running function
if (OfficeHelpers.Authenticator.isAuthDialog()) {
// Adding code here isn't guaranteed to run as we need to close the dialog
// Currently we have no realistic way of determining when the dialog is completely
// closed.
return;
}
// Create a new instance of Authenticator
var authenticator = new OfficeHelpers.Authenticator();
authenticator.endpoints.registerAzureADAuth('a11aaa11-1a5c-484a-b1d6-86c298e8f250', 'mytenant.onmicrosoft.com');
// Add event handler to the button
$('#login').click(function () {
$('#token', window.parent.document).text('Authenticating...');
authenticator.authenticate('AzureAD', true)
.then(function (token) {
// Consume and store the acess token
$('#token', window.parent.document).text(prettify(token));
authToken = token.access_token;
})
.catch(function (error) {
// Handle the error
$('#token', window.parent.document).text(prettify(error));
});
});
});
};
Now the code in the add-in can properly sign in the user and ask for the required permissions, but after clicking the Accept button on the application authorization step the following error is returned:
AADSTS50011: The reply address 'https://mywebapp.azurewebsites.net' does not match the reply addresses configured for the application: 'a11aaa11-1a5c-484a-b1d6-86c298e8f250'. More details: not specified
The error now returns every time I click the Login button (the user is no longer prompted to sign in). It never did retrieve the token. The full auth URL is:
https://login.windows.net/mydomain.onmicrosoft.com/oauth2/authorize?response_type=token&client_id=a11aaa11-484a-b1d6-86c298e8f250&redirect_uri=https%3A%2F%2Fmywebapp.azurewebsites.net&resource=https%3A%2F%2Fgraph.microsoft.com&state=982599964&nonce=3994725115
What am I doing wrong? Could the issue actually be because the host name of the web app (the redirect URI) does not match the domain of the Azure AD tenant hosting the app registration? If so, how can I grant permissions to Exchange Online from my Azure subscription hosting the web app which does not have Office 365 or Exchange Online? Would I have to add an Azure subscription to my test Office 365 tenant so that it can also host a web application??
From your app manifest, I found that you used https://myapp.azurewebsites.net/MessageRead.html as one of the replyUrls.
And below is the url that you are using to get consent from user.
https://login.windows.net/mydomain.onmicrosoft.com/oauth2/authorize?response_type=token&client_id=a11aaa11-484a-b1d6-86c298e8f250&redirect_uri=https%3A%2F%2Fmywebapp.azurewebsites.net&resource=https%3A%2F%2Fgraph.microsoft.com&state=982599964&nonce=3994725115.
If you observe above url, you mentioned redirect_uri as https://myapp.azurewebsites.net. But redirect_uri should match with at least one of the replyUrls you mentioned in the app manifest.
Try to replace https://myapp.azurewebsites.net with https://myapp.azurewebsites.net/MessageRead.html in authorization url.
I have updated them in below url, if you want you can directly try below url.
https://login.windows.net/mydomain.onmicrosoft.com/oauth2/authorize?response_type=token&client_id=a11aaa11-484a-b1d6-86c298e8f250&redirect_uri=https%3A%2F%2Fmywebapp.azurewebsites.net%2FMessageRead.html&resource=https%3A%2F%2Fgraph.microsoft.com&state=982599964&nonce=3994725115

Resources