How to send Active Directory Group SID in SAML response from OKTA? - response

Is it possible to send AD Group SID in SAML response? If yes, how can that be achieved so that we can process that SID value in our SP?
However we were able to send AD User SID in SAML response after doing profile mapping of appuser.objectSid to user.manager(as we didn't find user.objectSid) and adding it to attribute statements.
Is there any other/better way to obtain AD User SID in SAML response or above is the only way to do it?

So - you have 2 questions here:
1) Group SID. Okta does have Group SID but it isn't exposed as part of SAML config. You can send name(s) of groups that the user is a member of as part of the group attribute statement configurable in the SAML configuration of the app wizard (https://support.okta.com/help/articles/Knowledge_Article/Using-the-App-Integration-Wizard#Config_SAMLSettings)
2) For user SID, it's a 2 part setup. First, you want to add a custom attribute to the Okta user profile. That will allow you to then map the appuser.objectSid from AD to this custom attribute - rather than overloading some existing okta attribute that doesn't make sense. THen in the app wizard SAML config, you can add a new attribute to your SAML assertion and associate that with the newly added custom attribute.
Both group/user attribute statement config is done here (app wizard SAML config screenshot)
Hope this helps.

Related

Google oauth2Client.getToken is not returning id_token for other users

I'm implementing Google's 'code model' of Oauth2 and having trouble getting users' email - I wonder if this is a scopes problem or my misunderstanding about how to set up the code model. This sequence of events is already working:
Client loads https://accounts.google.com/gsi/client
Client starts call to google.accounts.oauth2.initCodeClient
Client gets code
Client passes code to one of my server endpoints
Server has an oauth2Client set up using the config with client_id, client_secret, and redirect URL = 'postmessage'
Server exchanges the code from the client for tokens
Server does oauth2Client.setCredentials(tokens) - this contains an access_token, which is enough for the client to make API calls to, e.g., retrieve the user's Google Calendar
Server is able to do oauth2Client.getTokenInfo(tokens.access_token);
There are various places along the way that involve scopes; I am probably getting something confused here. The client's initial call (step 2 above) uses
scope: 'https://www.googleapis.com/auth/calendar',
My code path on the server does define scopes anywhere.
In GCP, my project is set up with scopes
calendar.calendarlist.readonly, calendar.readonly and calendar.events.readonly
openid
/auth/userinfo.email
Here's the problem I'm encountering: when I go through this flow as a user and oauth with the account that owns the GCP project (this is a Google Workspace email, in case that matters), the tokens object that the server receives (step 6 above) has access_token, refresh_token and id_token - the id_token can be decoded to yield the user's email, and the user's email is also in the response to oauth2Client.getTokenInfo(token.access_token).
However, when I go through the flow with my other (personal) Gmail account, the tokens object that the server receives is missing the id_token but has the access and refresh tokens. Question 1: why are the responses different?
Question 2: How can I get the email of the user on the server in the personal Gmail account case? I've tried having the server make a call to https://www.googleapis.com/oauth2/v2/userinfo?fields=id,email,name,picture with the access_token, but this fails. I am not sure if I'm supposed to declare scopes for oauth2Client somehow, or tap a Google API using a different method on the server.
I think I've had a breakthrough: in step 2 in my original post, when I did "Client starts call to google.accounts.oauth2.initCodeClient", I had set the scope of initCodeClient to just the calendar scope. When I changed it instead to scope: 'https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/userinfo.email openid', (scope takes a space-delimited list in this case), it allowed my server call to get the id_token for this user and oauth2Client.getTokenInfo to get a response with the user's email in it.
When I updated the scopes like that, the popup asking for authorization also updated to request all the scopes I wanted - previously, it was only asking for the Calendar scope, so it makes sense Google didn't want to return the email.
What I still don't understand is why my previous setup was working for the account that owns the GCP project. In other words, when I was first building it out with that owner account, the client was only noting the Calendar scope while the server was asking for all three scopes (ie there was a mismatch), and the server was still able to get an id_token and the user's email in getTokenInfo. Maybe the owner account has some special privilege?

Pass field values from mvc application to the b2c sign up custom policy filelds

I have email field in my MVC application. Once user given email-Id we are validating and have to pass the same email-id to the Azure B2C SignUp policy. For this I have created custom policy. Can any body help me how I can achieve this.
See another answer at here for how this can be implemented using a custom policy.
It requires the email address to be passed as an input claim from the relying party application to the custom policy in a JWT that is signed with the client secret of the relying party application.
A working sample of this is here.
I was able to do this using a custom B2C policy.
I had a predefined extension attribute called registrationNumber that I wanted to pre-populate on my SignUp policy. Here is how to create a custom attribute.
I added registrationNumber as a ContentDefinitionParameter on the SignUp Policy.
<ContentDefinitionParameters>
<Parameter Name="registrationNumber">{OAUTH-KV:registrationNumber}</Parameter>
</ContentDefinitionParameters>
Still in the SignUp Policy, I added an output claim for the extension attribute.
<OutputClaim ClaimTypeReferenceId="extension_RegistrationNumber" AlwaysUseDefaultValue="true" DefaultValue="{OAUTH-KV:registrationNumber}"/>
Then, in my MVC app I add the parameter on redirect.
OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification notification)
notification.ProtocolMessage.Parameters.Add("registrationNumber", registrationNumber);
EDIT: As Chris Padgett points out, this is only applicable for pre-populating the sign-in email address, but the need was for sign-up. Chris's answer is better for sign-up.
If I understand correctly, your MVC application is the relying party that is redirecting to B2C for authentication and you want to prepoulate the email address box in B2C with the email address you collected in the MVC application.
The mechanism you are looking for is the OpenID Connect login_hint query string parameter in the authentication request that is sent to the B2C autorization endpoint.
B2C supports this parameter and provides instructions on how to read the login_hint with custom policy.
During a sign-in user journey, a relying party application may target
a specific user or domain name. When targeting a user, an application
can specify, in the authorization request, the login_hint query
parameter with the user sign-in name. Azure AD B2C automatically
populates the sign-in name, while the user only needs to provide the
password.
If you are using a custom policy, override the
SelfAsserted-LocalAccountSignin-Email technical profile. In the
section, set the DefaultValue of the signInName claim to
{OIDC:LoginHint}. The {OIDC:LoginHint} variable contains the value of
the login_hint parameter. Azure AD B2C reads the value of the
signInName claim and pre-populates the signInName textbox.
I don't know exactly how your application is built, but I'll assume that your MVC application is similar to the TaskWebApp described in Quickstart: Set up sign-in for an ASP.NET application using Azure Active Directory B2C. If this is the case you need to make two changes.
First, in your AccountController SignUpSignIn() method (or wherever it is you have the validated email address and are ready to redirect to B2C), you need to add the email address to the OWIN context.
public void SignUpSignIn()
{
if (!Request.IsAuthenticated)
{
var validatedEmailId = "emailaddress#example.com"; //read from form
HttpContext.GetOwinContext().Set("validatedEmail", validatedEmailId);
HttpContext.GetOwinContext().Authentication.Challenge();
return;
}
Response.Redirect("/");
}
Then in your Startup.Auth.cs, you need to modify the OnRedirectToIdentityProvider() callback to read the email address from the environment and use that as the LoginHint value.
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var policy = notification.OwinContext.Get<string>("Policy");
if (!string.IsNullOrEmpty(policy) && !policy.Equals(DefaultPolicy))
{
notification.ProtocolMessage.Scope = OpenIdConnectScopes.OpenId;
notification.ProtocolMessage.ResponseType = OpenIdConnectResponseTypes.IdToken;
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), policy.ToLower());
}
else //default, sign-in/sign-up
{
notification.ProtocolMessage.LoginHint =
notification.OwinContext.Get<string>("validatedEmail");
}
return Task.FromResult(0);
}

Restricting api access by client

I have an identity server built with Identity server 4. There is one main API with several angular web applications build by third party customers that access this API endpoint.
Now I would like to create a second API but its only for internal use with OUR official plugin. I am trying to figure out how to lock it down so that only our app can access it. I am not a fan security by obscurity and assuming that the third party's dont know its there so wont try and access it.
My first thought was to add a new scope for this API but by doing that its going to popup and ask the users for access to the data which isnt really need.
The only thing i can think of would be to check the client id some how in the API and add a policy for it. This really isnt right ether as to my understanding polciy should be checking stats of the user and not the client itself.
services.AddAuthorization(
options => {
options.AddPolicy("DevConsole", policy => IsClientId(xxxx)
}
);
Is it possible to lock down an API based upon a single client id? or am i going at this in the wrong way.
Another idea i had was to add another claim if they login with this client id which seams like overkill to me.
Example:
Lets say that I have an API endpoint that allows you to update the usersname. All users have access to their name this isnt a scope issue. However only our official app has access to update the usersname. Any app created by third party developers do not have access to the endpoint to update a usersname.
So our official plugin has a client id of 123 and yours has a client id of 321. A user logged though client id 321 can not access this endpoint. User logged in though client id 123 can.
I am starting to think this isnt possible because Oauth and Openid are completely user based. There is no way to validate the user based upon the client they authenticated with.
if I understand the problem correctly, I would create a new Client on Identity Server, for the "main API" and a new Resource for the "internal API"
This would allow the "main API" to also be a client, with client credentials grant type, therefore it has a id+secret and is allowed to request a token for itself. In this case, you will now request the newly created scope for in "internal API" and the users will have no knowledge that this entity evens exists.
After going back and forth with this with this it occurred to me that the client id is returned as a claim. So when i got in this morning i checked.
This should enable me to add a policy for only our official plugin.
services.AddAuthorization(options =>
{
options.AddPolicy("IsOfficalApp", policy => IsCheckOfficalClient());
});
This should enable me to lock down the API endpoints in question without requiring additional authorization from the users.

Using immediate=true in Salesforce OAuth flow

Previously asked this question in the Salesforce StackExchange which they considered off-topic so asking here to see if I can get an answer.
Background
I am attempting to use the immediate parameter to check if a Salesforce user has already approved access when going through the Web Server OAuth Flow as documented on OAuth 2.0 Web Server Authentication Flow. My reasoning for this is that I do not want the login or consent prompts to appear so I can reject access if they have not already approved.
Once the callback page is hit, I am always receiving the parameter error=immediate_unsuccessful even if the user has approved the application before and is logged in.
I have attempted to check this via a customised Google OAuth 2 Playground and setting immediate=true or immediate=false to the end of the authorize endpoint. On =false, the consent prompt shows and then you can grant access. On =true, this returns the same error as listed previously.
The Connected App that has been set up has api and refresh_token as the available scopes, users are able to authorize themselves and there are no ip restrictions set. The client id and secret from this app is then passed into the OAuth 2 Playground.
Below is a brief example on how my proper application redirects to the auth url using Java and the Google OAuth client library. We initially authorize the client without the immediate and then later on call the same code with immediate=true (shown in example)
AuthorizationCodeFlow authorizationCodeFlow = new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(),
httpTransport,
GsonFactory.getDefaultInstance(),
new GenericUrl("https://login.salesforce.com/services/oauth2/token"),
new ClientParametersAuthentication(CLIENT_ID, CLIENT_SECRET),
CLIENT_ID,
"https://login.salesforce.com/services/oauth2/authorize")
.setCredentialDataStore(StoredCredential.getDefaultDataStore(MemoryDataStoreFactory.getDefaultInstance()))
.build();
AuthorizationCodeRequestUrl authUrl = authorizationCodeFlow.newAuthorizationUrl()
.setRedirectUri("https://72hrn138.ngrok.io/oauth/callback")
.setScopes(ImmutableSet.<String> of("api", "refresh_token"))
.set("prompt", "consent")
.set("immediate", "true");
response.redirect(authUrl);
Question(s)
Are there any settings that I may have missed in Salesforce that would alleviate the error?
Is there any other option in the OAuth 2 spec that has to be set for the immediate option to work?
Does the immediate setting work?
I managed to solve this issue in the end. To allow the immediate=true option to work, the scopes have to be removed from the request. In the example provided you would amend the authUrl to the following:
AuthorizationCodeRequestUrl authUrl = authorizationCodeFlow.newAuthorizationUrl()
.setRedirectUri("https://72hrn138.ngrok.io/oauth/callback")
.set("prompt", "consent")
.set("immediate", "true");
I believe the theory is that defining a scope means you are asking for permissions to use those scope and therefore requires approval for those permissions. This clashes with the immediate option which states that the user must be logged in and the client id already been approved for it to succeed.

OWIN OAuth 2.0 Authorization Server + custom membership provider

I have a working site aaa.com with custom membership provider
connected as http module.
I need a create OAuth 2.0 Authorization
Server + Resource Server
Main scenario of using is Authorization
Code Grant.
Here is a good example of creating what I need http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server (see source code of example)
What is my problem:
I want to bind Authorization Server to subdomain of main site -
oauth.aaa.com and when user came from external site to my
Authorization Server, this server check if user is already
authenticated on main site(aaa.com) and if yes - he must just press
Grant button and allow external site access, if no - he must enter user name and password and after this he must automatically be log in
to main site and Authorization Server must be authenticated too.
I've already connected my custom membership provider as http
module(another variant of using not impossible now) to Authorization
Server and Authorize action of this server is already authenticated
when user already logged in to main site.
I don't understand how I must configure Authorization Server(or create some customizations) that it can accept a cookie from main
site(or automatically synchronize and create a new cookie for
Authorization Server using info from already authenticated request).
I was try to configure CookieAuthenticationOptions with same cookie name as cookie name of main site and check that in cookie of main site domain is .aaa.com ...but nothing happend and variable ticket in code = null
var authentication = HttpContext.GetOwinContext().Authentication;
var ticket = authentication.AuthenticateAsync(Auth.AuthenticationType).Result;
Or may be I need another library, not based on OWIN? I saw DotNetOpenAuth library but seems its OAuth client, not server.

Resources