I'm trying to use Okta SSO to authenticate my users in AspNetZero app.
When I click on the OpenID in the bottom of the login page, I'm redirected to Okta login and then I'm back to my app login page and finally "openIdConnectLoginCallback" is calling "ExternalAuthenticate" on the TokenAuthController and the call to _externalAuthManager.GetUserInfo throw exception "name claim is missing".
In "login.service.ts", claims array contain "name" claim and ExternalAuthenticateModel.ProviderAccessCode contains a valid JSon Token and I check and "name" is in the token.
let claims = this.oauthService.getIdentityClaims();
const model = new ExternalAuthenticateModel();
model.authProvider = ExternalLoginProvider.OPENID;
model.providerAccessCode = this.oauthService.getIdToken();
model.providerKey = claims['sub'];
model.singleSignIn = UrlHelper.getSingleSignIn();
model.returnUrl = UrlHelper.getReturnUrl();
Here is my appsettings.json
Here is my app configuration in Okta
Any ideas to fix that "name" claim missing ?
Related
I have an application using .NET Core 3.1 MVC Web App that uses Azure AD B2C to sign in users and I've just migrated it to use Microsoft Identity Web library.
We want to have two different policies for Sign In, one for regular users (B2C_1A_SignUpOrSignIn) and one for admin users (B2C_1A_SignInAdmin).
So, in the Appsettings, we have the following format:
"AzureAdB2C": {
"Instance": "https://url.b2clogin.com/tfp/",
"ClientId": "clientId",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout/B2C_1A_SignUpOrSignIn",
"Domain": "url.onmicrosoft.com",
"Domain_b2cLogin": "url", // Required by the Cookie Policy
"SignUpSignInPolicyId": "B2C_1A_SignUpOrSignIn",
"SignInAdminPolicyId": "B2C_1A_SignInAdmin",
"ResetPasswordPolicyId": "B2C_1A_PasswordReset",
"EditProfilePolicyId": "",
"ClientSecret": key,
"B2cExtensionAppClientId": "key"
},
In the Startup class, I just added the following:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration, "AzureAdB2C");
And I also overrode the "AzureController", so, for a regular user, I use the same method obtained from
here Microsoft Identity Web - Account Controller "SignIn". However, for an admin user, I changed that method to use something similar to what is provided by the PasswordReset method, as the following:
public IActionResult SignInAdmin()
{
string scheme = OpenIdConnectDefaults.AuthenticationScheme;
var redirectUrl = Url.Content("~/");
var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
properties.Items[Constants.Policy] = "B2C_1A_SignInAdmin";
return Challenge(properties, scheme);
}
So, as you can see, I'm using a different policy name for this method.
Everything seems to work fine, the user is redirected to the correct login page based on the policy and the token is issued by Azure and our application accepts the Token, in our method
options.Events.OnTokenValidated = context => {}
However, soon after that, something goes wrong with the authentication and the method
options.Events.OnRemoteFailure
is called with the following exception
"{"Message contains error: 'invalid_grant', error_description: 'AADB2C90088: The provided grant has not been issued for this endpoint. Actual Value : B2C_1A_SignUpOrSignIn and Expected Value : B2C_1A_SignInAdmin ..."
So, my question is, what do I have to do to be able to use two different policies to sign in? Or is there any configuration that I should do to be able to do that?
Thank you in advanced.
I guess the B2c Middleware validates the "tfp" claim. This should usually match the SignInPolicyId. You might have to override the TokenValidation to let both policies (tfp = trustedFrameworkPolicy) be valid
I am trying to control authorization via app registrations in Azure.
Right now, I have two app registrations set up.
ApiApp
ClientApp
ApiApp is set up with the default settings, but I have added this to the manifest:
"oauth2Permissions": [
{
"adminConsentDescription": "Allow admin access to ApiApp",
"adminConsentDisplayName": "Admin",
"id": "<guid>",
"isEnabled": true,
"type": "User",
"userConsentDescription": "Allow admin access to ApiApp",
"userConsentDisplayName": "Admin",
"value": "Admin"
},
...
]
In the client app registration, I have all the defaults, but I added:
In the keys, a password for authenticating the app against AD
In required permissions, I added ApiApp and required the delegated permission "Admin." I saved that, clicked done, then I clicked "Grant Permissions" to make sure the permissions had a forced update.
In my client app, it uses this code for authentication purposes:
...
var context = new AuthenticationContext(authority);
var clientCredentials = new ClientCredential(<clientId>, <clientSecret>);
var result = await context.AcquireTokenAsync(<apiAppUri>, clientCredentials);
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
var webResult = await client.GetAsync(<api uri>);
My ApiApp is just using the built in authorization if you select work or school accounts when you create a Web API project:
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
This works:
[Authorize]
public class ValuesController : ApiController
These do not work:
[Authorize(Users = "Admin")]
public class ValuesController : ApiController
or
[Authorize(Roles= "Admin")]
public class ValuesController : ApiController
Based on what I'm reading, I believe I have everything set up appropriately except the ApiApp project itself. I think I need to set up the authorization differently or with extra info to allow the oauth2Permission scopes to be used correctly for WebAPI.
What step(s) am I missing to allow specific scopes in WebAPI instead of just the [Authorize] attribute?
I used Integrating applications with Azure Active Directory to help me set up the app registrations, along with Service to service calls using client credentials , but I can't seem to find exactly what I need to implement the code in the Web API part.
UPDATE
I found this resource: Azure AD .NET Web API getting started
It shows that you can use this code to check out scope claims:
public IEnumerable<TodoItem> Get()
{
// user_impersonation is the default permission exposed by applications in Azure AD
if (ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope")
.Value != "user_impersonation")
{
throw new HttpResponseException(new HttpResponseMessage {
StatusCode = HttpStatusCode.Unauthorized,
ReasonPhrase = "The Scope claim does not contain 'user_impersonation' or scope claim not found"
});
}
...
}
However, the claims I get do not include any scope claims.
You have to use appRoles for applications, and scopes for applications acting on behalf of users.
As per GianlucaBertelli in the comments of Azure AD, Scope-based authorization:
...in the Service 2 Service scenario, using the client credentials flow you won’t get the SCP field. As you are not impersonating any user, but the calling App (you are providing a fixed credential set).
In this case you need to use AppRoles (so Application permissions, not delegated) that results in a different claim. Check a great how-to and explanation here: https://joonasw.net/view/defining-permissions-and-roles-in-aad.
In the link he provides, it discusses appRoles in the application manifest.
The intention behind the resources I was looking at before was to allow users to login to the client application, and then the client application authenticates against the API on behalf of the user. This is not the functionality I was trying to use -- simply for a client application to be able to authenticate and be authorized for the API.
To accomplish that, you have to use appRoles, which look like this in the application manifest:
{
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Read all todo items",
"id": "f8d39977-e31e-460b-b92c-9bef51d14f98",
"isEnabled": true,
"description": "Allow the application to read all todo items as itself.",
"value": "Todo.Read.All"
}
]
}
When you set the required permissions for the client application, you choose application permissions instead of delegated permissions.
After requiring the permissions, make sure to click the "Grant Permissions" button. To grant application permissions, it requires an Azure Active Directory admin.
Once this is done, requesting an access token as the client application will give you a "roles" claim in the token, which will be a collection of string values indicating which roles the application holds.
I am new at this. Can someone please help me, since I am going crazy over my problem for nearly a month now :(
In short: I have identity server project, an webapi project and angular client. Client request to authenticate and gets id_token and access_token (all good), access_token send to webapi project where I have:
var idServerBearerTokenAuthOptions = new IdentityServerBearerTokenAuthenticationOptions {
Authority = "https://localhost:11066/IdentityServer/identity",
ValidationMode = ValidationMode.ValidationEndpoint,
AuthenticationType = "Bearer",
RequiredScopes = new[] { "permissions", "openid" },
DelayLoadMetadata = true
};
app.UseIdentityServerBearerTokenAuthentication(idServerBearerTokenAuthOptions);
and I have Autofac which should get me the current logedin user
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
builder.Register(c => new ClaimsIdentityApiUser((ClaimsIdentity)Thread.CurrentPrincipal.Identity)).As<IApiUser>().InstancePerRequest();
BUT Thread.CurrentPrincipal.Identity has nothing, and
also ClaimsPrincipal.Current.Identity has nothing. What am I missing??
p.s. Similar problem to this question Protecting webapi with IdentityServer and Autofac - can't get claims but obviously not same solution nor set up.
a) user should always be retrieved from ApiController.User (or the RequestContext)
b) token validation might fail for some reason use this resource to enable logging for the token validation middleware:
https://identityserver.github.io/Documentation/docsv2/consuming/diagnostics.html
c) are you using JWTs or reference tokens? For JWTs you can set the ValidationMode to Local
I registered an app as a valid admin in my network.
I got my token.
when i request a list of tokens to impersonation I receive only my token and not all the tokens (of users in my network).
This is the request I'am making:
https://www.yammer.com/api/v1/oauth/tokens.json?consumer_key={0}&user_id={1}", CONSUMER_KEY, UserId
Where {0} = My Application Client Id,
Where {1} = My UserId (The verified admin user id).
what could i be doing wrong?
Thanks guys.
I found the answer.
My error was I was registered under
"xxx.com"
and when I requested the token I was under the domain of:
"yyy.com"
so the token I received was not the right token.
Another mistake I made was trying to impersonate verified admins - a verified admin can't impersonate another verified admin.
**
Solution:
Register a new app with a user that is registered on the right domain.
Get the token with thee user that is registered on the right domain.
Get id's of users that are not verified admin.
**
Thanks for all your help.
Hope this info contribute to someone.
You can pass in user_id the sender_id from the post object
var config = require('../config');
module.exports = function (sender_id, tk) {
var params = '?' + 'user_id=' + sender_id + '&consumer_key=' + config.YAMMER_CONSUMER_KEY + '&access_token=' + tk;
var url = 'https://www.yammer.com/api/v1/oauth/tokens.json' + params;
return url;
};
The Google+ API is set to "On" from google's developer console. I am fetching the profile information of the user by supplying the api key but I get an error saying:
Access Not Configured. Please use Google Developers Console to activate the API for your project. [403]
BaseClientService.Initializer ini = new BaseClientService.Initializer { ApiKey = "" };
PlusService plusService = new PlusService(ini);
if (plusService != null)
{
PeopleResource.GetRequest prgr = plusService.People.Get("me");
Person googleUser = prgr.Execute();
}
The error is thrown when Execute is called.
Does this service needs to be set up with "billed" profile ? This may be the reason I am getting access error.
The "me" argument only works when you have an authenticated user's OAuth 2.0 access token. Simple API access with a key only allows access to public data - try putting in a numeric user id instead of me and you should get a response.