I'm trying to integrate with Okta SSO by implementing SAML 2.0 in my website as Service Provider (SP) and Okta env. as my Identity Provider (IDP)
I can't understand how to configure my IDP to return for each Auth request, the groups a user is in. How can it be done?
Also, Is it possible to have service account in my IDP that my backend can ask the IDP directly if a user is inside some specific group?
It is possible to add groups to the SAMLResponse by configuring the SP App in the Okta admin dashboard correctly.
In order to do it for an existing app, Go to Admin panel and edit the SAML settings to include a Group attribute statements.
For instance, If you want to expose all groups containing the word admin to your SP, add a field with a proper name (i.e groups) and specify a regex filter with value .*admin.*.
The SAMLResponse will contain the following node after configuring correctly:
<saml2p:Response
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
......
......
<saml2p:Status
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
<saml2p:StatusCode
Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</saml2p:Status>
<saml2:Assertion
......
......
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
<saml2:AttributeStatement
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:Attribute
Name="groups"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="xs:string">admins_group_1
</saml2:AttributeValue>
<saml2:AttributeValue
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="xs:string">it_admins
</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
</saml2p:Response>
Note that groups will contain all groups containing the word admin, no matter if its an Okta group, AD group etc..
Related
Have an API in Azure API Management (APIM).
The API operation validates a JWT generated by Azure Active Directory (AAD) using a scope from a backend app registration (the scope is NOT User.Read). Note: client id is another app registration which is an authorized app of that backend scope.
After the JWT is validated, am I able take that token, extract user info out of it and verify if the user is part of a email distribution list (DL)? If so, how to do it within an APIM policy?
I am aware of MS Graph APIs. Using Postman I can confirm the DL is listed in the tenant's groups and can get its group ID. I can also confirm the user is a member of the group. The bit I'm stuck with for Graph API is that it needs a different token to the one supplied by the client application (due to he scopes being from different domains custom app registration vs graph) and I'm stuck at this point. Should I make the client app also get a graph token and pass it in a separate header, or is there way to orchestrate things from within APIM or something else?
The non-APIM part of this solution is provided by a Microsoft article. I've summarised those and combined with the APIM parts in the following steps:
In Azure, create a new Azure App Registration (note the client id for later)
Under "Certificates and secrets", add a client secret (note the secret for later)
Under "API Permissions", add a new MS Graph Application Permission (can be User.Read.All, Group.Read.All, GroupMember.Read.All depending on your situation). MS Graph's "groups" includes both AD groups and Distribution Lists (DL). Note: don't use Delegated permission.
Application permissions allow the authorized app to enquire about any user/group. You will need an Azure Admin to Grant Admin Consent for the App Registration to have the chosen Application Permission.
Now in Azure APIM, go to your API and edit the inbound policy.
Validate the JWT from the user making the call (See validate-jwt or newer validate-azure-ad-token) to ensure the User is authorized to call this API.
Extract the oid claim from the JWT (this is the user ID I'll use for the graph call) and save it in a variable using set-variable policy
Add a send-request policy request an auth token for MS Graph using client-credentials flow (this is when you'll need the client id and secret from earlier App registration). Note: secrets should be stored in a secure store like KeyVault but that is outside the scope of this answer.
Extract the access_token field from the JSON response body and put it in a variable using set-variable policy.
Create another send-request policy, but this time to the MS Graph endpoint. For User.Read.All permission you'd use /users/<userIdFromJwtOidClaim>/memberof/<groupId>. MS Graph v1.0 API Reference, and pass the access_token in the Authorization header using <set-header> element.
A status code of 200 indicates the user is a member of the group. IIRC A status code of 403 indicates the user isn't a member of the group.
Use a choose policy to perform logic depending on the user's group membership.
Use return-response policy to send a response back to the user.
I have tried using the sso url as mentioned "Single Signon Service"https://auth.pingone.asia/{env}/saml20/idp/sso , and issuer as https://auth.pingone.asia/{env}. but getting ErrorCode: INVALID_ISSUER - Unable to find application for spEntityId: 'https://auth.pingone.asia/{env}' in environment {env}.
Please help me to understand where exactly I have wrong configuration.
Check if you have created a SAML Application, if not, try creating it with https://apidocs.pingidentity.com/pingone/platform/v1/api/#post-create-application-saml-protocol
Note: acs url is the place where you want PingIdentity to redirect after login authentication is completed. You will get a SAMLRespose also posted there.
(OR) You can create SAML application directly from the PingIdentity console itself by selecting Add Application, Select SAML as type, and Set Manual Saml configuration by specifying entity id (something unique) and ACS url.
Go to ping console dashboard, and check the Entity ID of the application, it should match with your AuthnRequest saml:Issuer
Example if your dashboard is like this with Entity ID "test",
then your AuthnRequest should look like this:
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="test"
Version="2.0"
IssueInstant="2022-09-19T16:46:59">
<saml:Issuer>test</saml:Issuer>
</samlp:AuthnRequest>
Note the line: <saml:Issuer>test</saml:Issuer>
I am facing a scenario where my .NET Core app (Azure Web API) will be accessed in two ways:
client_credentials flow
delegated permissions (user) flow
I am using Microsoft Identity Web to authorize and authenticate requests via AD on middleware level. Then I would like to do additional authorization inside my controller methods to check the following:
In case of application call (client credentials), check that the provided access_token contains a specific roles claim that matches with the application role defined in app registrations -> app roles.
In case of signed-in user call, check that the provided access_token contains specific AD Groups (security groups) assigned to that user in Azure AD.
Flow #1 works, but if I enable flow #2 by clicking on token configuration -> add groups claim -> Security Groups -> emit groups as role claims in access token, then the app roles are no longer available in the client credentials flow inside the access token (as in the below screenshot), presumably because it overwrites the roles claim with the security groups (which do not exist for applications).
What is the correct way to do this, or achieve an equivalent situation in a different way?
The requirement is to differentiate controller method access where application A can call e.g. a read-only endpoint 1, but cannot call write endpoint 2, whereas application B is able to call write endpoint 2. The same differentiation should be done also for users on AD-group basis.
You can include a groups claim in your token. You just need to modify the "groupMembershipClaims" field in application manifest:
"groupMembershipClaims": "SecurityGroup"
Then the token will contain the Ids of the groups that the use belongs to like below :
{
"groups": ["{group_id}"]
}
This method won't overwrite the roles claim. So for delegated permissions (user) flow, you need to check the groups claim instead now.
I implemented what is described here: Signup with email invitation
This works flawlessly when the Azure app service uses the B2C Azure AD for authentication\login, that is, the invitation e-mail is sent when I'm logged in through an account authenticated by the B2C AD.
Now I have another app service pointing to a B2B Azure AD for user login\authentication and so the invitation e-mail is sent when I'm logged in through an account authenticated by the B2B AD.
The email with the invitation link that points to the B2C AD is sent correctly. However after clicking the link, the B2C signup invitation policy takes action and I'm presented with a 401 error (unauthorized). This looks like the B2C policy can't access the metadata endpoint set in the policy configs for any reason...
The message being displayed is:
Correlation ID: 78292d04-7184-42d0-ac2d-a60bb98a532f
Timestamp: 2019-09-20 16:19:25Z AADB2C: The metadata endpoint
'https://mywebsite.azurewebsites.net/.well-known/openid-configuration'
returned the following status code: '401'
This is the custom signup invitation policy <ClaimsProvider> in which this metadata endpoint is specified:
<!--This technical profile specifies how B2C should validate the token, and what claims you want B2C to extract from the token.
The METADATA value in the TechnicalProfile meta-data is required.
The “IdTokenAudience” and “issuer” arguments are optional (see later section)-->
<ClaimsProvider>
<DisplayName>ID Token Hint ClaimsProvider</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="IdTokenHint_ExtractClaims">
<DisplayName>ID Token Hint TechnicalProfile</DisplayName>
<Protocol Name="None" />
<Metadata>
<!-- Action required: replace with endpoint location -->
<Item Key="METADATA">https://mywebsite.azurewebsites.net/.well-known/openid-configuration</Item>
<!-- <Item Key="IdTokenAudience">your_optional_audience_override</Item> -->
<!-- <Item Key="issuer">your_optional_issuer</Item> -->
</Metadata>
<OutputClaims>
<!--Read the email claim from the id_token_hint-->
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="shardTenantId"/>
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
My question is: why this 401 (unauthorized) is being thrown? When I try to access the metadata endpoint: https://mywebsite.azurewebsites.net/.well-known/openid-configuration it opens just fine and returns the required json output:
{
issuer: "https://mywebsite.azurewebsites.net/",
jwks_uri: "https://mywebsite.azurewebsites.net/.well-known/keys",
id_token_signing_alg_values_supported: [
"RS256"
]
}
This is the web API method that outputs the json described above:
[AllowAnonymous]
[Route(".well-known/openid-configuration", Name = "OIDCMetadata")]
[HttpGet]
public HttpResponseMessage Metadata()
{
var json = JsonConvert.SerializeObject(new OidcModel
{
// The issuer name is the application root path
Issuer = InvitationHelper.GetBaseUrl(),
// Include the absolute URL to JWKs endpoint
JwksUri = Url.Link("JWKS", null),
// Sample: Include the supported signing algorithms
IdTokenSigningAlgValuesSupported = new[] { SigningCredentials.Value.Algorithm }
});
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;
}
I even tried adding [AllowAnonymous] annotation to the API method but that didn't make any change.
Why can't the custom signup invitation policy access the metadata endpoint that is in an app service hosted on a different B2B AD tenant?
NOTE 1: added Application Insights instrumentation to this custom policy and the only message I saw there was Fatal Exception 401 without any more valuable info:
{"Kind":"FatalException","Content":{"Time":"4:15 PM","Exception":{"Kind":"Handled","HResult":"80131500","Message":"The
metadata endpoint
'https://mywebsite.azurewebsites.net/.well-known/openid-configuration'
returned the following status code:
'401'","Data":{"IsPolicySpecificError":false,"uri":"https://mywebsite.azurewebsites.net/.well-known/openid-configuration"}}}}
NOTE 2: if you look at the <ClaimsProvider> above there are these 2 settings:
<!-- <Item Key="IdTokenAudience">your_optional_audience_override</Item> -->
<!-- <Item Key="issuer">your_optional_issuer</Item> -->
I'm not sure how to use these other 2 settings as those were not described in the doc # GitHub.
Maybe the issuer is the problem... I'm trying things here but nothing is working so far. So I decided to ask this question. I hope someone can shed some light.
I got in contact with Jas Suri through LinkedIn. He's the guy that did the last commit in that GitHub repo.
He asked me for the real metadata endpoint (the one I posted here is a dummy URL).
When he tried to access that URL he was prompted to login... what!? Login!? Yep. That answered the question. I got SSO here on my developer box and that's why I didn't catch that... but wait: I had [AllowAnonymous] annotation in that web API method, right? So why was it still asking for login!?
He promptly asked me if I had enabled Authentication on the app service level... I went to Azure Portal and checked the app service Authorization/Authentication settings. Guess what... I had it turned ON but the problem was not that directly. See the highlighted setting below:
We can keep it on, but the real problem was that sometime ago I had set
Action to take when request is not authenticated
Log in with Azure Active Directory
That was overrides the [AllowAnonymous] annotation placed on the web API method.
As soon as I selected in the dropdown:
Allow Anonymous requests (no action)
and saved, then the B2C policy started working as expected.
Anonymous access is enabled on the App Service app. Users will not be
prompted for login.
I need help with configuring the Identifier(Entity ID) and Reply Url of my configuration in Azure for Active Directory SAML 2.0, I don't know how to configure them
I have tried to set them some value but I got this message:
AADSTS750054: SAMLRequest or SAMLResponse must be present as query
string parameters in HTTP requests for SAML Redirect binding.
Any ideas on how to make it works?
How to enable Azure AD SAML authentication depends on the SAML library you're using. Generally, the entityID and Reply URL are APP ID URI and Reply URL of your registered application in AAD. After you configured the library correctly, it will automatically help generate SAMLRequest for you.
Here is an configuration example of using Sustainsys.Saml2 library:
<!--entityId is APP ID URI of your registered application in AAD; Return URL is Reply URL-->
<sustainsys.saml2 entityId="https://www.contoso.org/checkresourceapp"
returnUrl="http://localhost:54664/"
>
<nameIdPolicy allowCreate="true" format="Persistent"/>
<identityProviders>
<add entityId="https://sts.windows.net/<your tenant id>/"
metadataLocation="https://login.microsoftonline.com/<your tenant id>/FederationMetadata/2007-06/FederationMetadata.xml"
loadMetadata = "true"
allowUnsolicitedAuthnResponse="true"
signOnUrl="https://login.microsoftonline.com/<your tenant id>"
binding="HttpRedirect">
</add>
</identityProviders>
<federations>
<add metadataLocation="https://login.microsoftonline.com/<your tenant id>/FederationMetadata/2007-06/FederationMetadata.xml" allowUnsolicitedAuthnResponse = "false" />
</federations>
</sustainsys.saml2>
Regarding complete interpretation, please refer to https://saml2.sustainsys.com/en/2.0/configuration.html
You are using the wrong URL here. For IDP initiated URL please find the "User Access URL" from the Azure AD Application Properties tab. Then you can use that URL in the browser and it will do the SSO. Also alternatively the user can log in to Access Panel https://myapps.microsoft.com and then click on the application tile to perform the SSO. In the Azure Portal got to Azure Active Directory -> Enterprise Apps -> Search your app -> Click on the app -> Properties page -> User Access URL