oidc-client-js double encoding acr_values? - identityserver4

I'm using oidc-client-js in an Angular application and would like to use the acr_values to pass an IDP value to Identity Server 4. (Identity Server is our primary token service, but we have configured it to use Okta as an external provider for one tenant of the application.)
Setting the value like this in Angular:
this.userManager = new UserManager({
authority: environment.stsAuthority,
client_id: window.location.hostname,
acr_values: 'ipd:oktatest',
...
The generated URL contains %26acr_values%3Dipd%253Aoktatest which is what you get if you URL encode idp:oktatest twice.
If I manually change the URL to %26acr_values%3Didp%3Aoktatest it works as expected. (It's hard to pick up on the difference but the % sign in %3A between ipd and okta becomes %25 when it's double encoded.)
Am I doing something wrong? Is this a bug? Is there a better way to specify the value of acr_values in the Angular code?

You can try this workaround by defining the acr_values at the level of signinRedirect:
this.options.acr_values = 'ipd:oktatest';
this.userManager.signinRedirect(this.options);
this.userManager = new UserManager({
authority: environment.stsAuthority,
client_id: window.location.hostname,
...});

Related

Cannot get Username / given_name when using angular-oauth2-oidc and Identity Server 4

I am following the Implicit Workflow example from the angular-oauth2-oidc documentation.
Everything works well in my Angular app, and I can login (during which I am redirected to Identity Server), get my token and use this token to access my Web Api.
However, I have noticed that the "given_name" claim is null, and therefore, the username is not displayed on the login page. Specifically, the following method from the sample code appears to return null:
public get name() {
let claims = this.oauthService.getIdentityClaims();
if (!claims) return null;
return claims.given_name;
}
I thought perhaps this was a problem with permissions, but my scope is set to:
scope: 'openid profile email api1',
Any idea what I need to change to get this "given_name" claim?
For those who encountered the same issue. You can fix it by adding this line AlwaysIncludeuserClaimsInIdToken=true in the client settings on identity provider side.
OauthService.getIdentityClaims() is a Promise and holds UserInfo you can extract the name field with braces, so your function should be:
public get name() {
let claims = this.oauthService.getIdentityClaims();
if (!claims) return null;
return claims['name'];
}
The answer marked as "Best answer" is not correct. Get the user claims in the 'idtoken' will cause that the 'idtoken' be very big and then you may exceed the size limit.
The correct implementation is to use the 'UserInfo' Endpoint and then use the method 'loadUserProfile':
Example:
getUserClaims() {
const user = this.oauthService.loadUserProfile();
console.log(user, user);
}
I had the same issue, in my case with an error displayed on the browser console, saying that Request was blocked by Security Policy.
even having the AllowAnyOrigin() method called in startup, I lacked to get the header allowed. So when in my angular aap i call the loadUserProfile method via the
token_received event, it sends some headers that were not allowed.
Finaly this fix my issue:
app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader());
Don't forget calling that before usemvc

Setting our app up as a Service Provider [SalesForce is the IdP, we are using AspNetSaml]

I think I'm close, but there's just something I'm missing:
GIVEN:
We just want to be able to open our app, get the UserID from salesforce (which would match the UserID in our app), and log us in ... seems easy enough.
We are trying to use the open source C# component AspNetSaml https://github.com/jitbit/AspNetSaml
I have a cloud instance that hosts our app, for testing:
The URL to get to our app would be something along the lines of: http://1.2.3.4/OurCompanyApp/app.wgx
SALESFORCE:
I didn't initiate this process, but I do see that in Salesforce we have it setup as an Identity Provider. So I see this information under Identity Provider:
Issuer: https://our-company-stuff.my.salesforce.com
Label: SelfSignedCert_04Nov2016_195856
Unique Name: SelfSignedCert_04Nov2017_195856
Key Size: 2048
Salesforce Identity: https://our-company-stuff.my.salesforce.com/.well-known/samlidp.xml
I did set up a Service Provider via Connected Apps:
Connected App Name: AppTest
API Name: AppTest
Description: AppTest
Start URL: http://1.2.3.4/OurCompanyApp/app.wgx
Enable SAML: true
Entity ID: http://1.2.3.4/OurCompanyApp/app.wgx
ACS URL: http://1.2.3.4/OurCompanyApp/samlconsume.wgx
Enable Single Logout: false
Subject Type: User ID
Name ID Format: urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
IdP Certificate: Default IdP Certificate
ACTUAL CODE:
//var samlEndpoint = "https://csi1-dev-ed.my.salesforce.com/";
//var samlEndpoint = "https://csi1-dev-ed.my.salesforce.com/.well-known/samlidp.xml";
(one of these would be un-commented)
var request = new AuthRequest(
"http://1.2.3.4/OurCompanyApp/app.wgx", //put your app's "unique ID" here
"http://1.2.3.4/OurCompanyApp/samlconsume.wgx" //assertion Consumer Url - the URL where provider will redirect authenticated users BACK
);
string url = request.GetRedirectUrl(samlEndpoint);
Response.Redirect(url);
GetRedirectUrl encrypts this bit of xml:
xw.WriteStartElement("samlp", "AuthnRequest", "urn:oasis:names:tc:SAML:2.0:protocol");
xw.WriteAttributeString("ID", _id);
xw.WriteAttributeString("Version", "2.0");
xw.WriteAttributeString("IssueInstant", _issue_instant);
xw.WriteAttributeString("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
xw.WriteAttributeString("AssertionConsumerServiceURL", _assertionConsumerServiceUrl);
xw.WriteStartElement("saml", "Issuer", "urn:oasis:names:tc:SAML:2.0:assertion");
xw.WriteString(_issuer);
xw.WriteEndElement();
xw.WriteStartElement("samlp", "NameIDPolicy", "urn:oasis:names:tc:SAML:2.0:protocol");
xw.WriteAttributeString("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
xw.WriteAttributeString("AllowCreate", "true");
xw.WriteEndElement();
The URL Returned, that I redirect to, would be something along the lines of (depending upon which endpoint I tried, I tested with both):
https://our-company-stuff.my.salesforce.com/?SAMLRequest=jZJdT8IwGIX%2fStP77oMx2Bq2BCHGJagLTC%2b8Md1WpEnXzn5M%2ffeWgQleSLxr3pxznvO%2b6UKTjvd4ac1BbOm7pdqAYp3B11kwn8SzZI%2bSuG3RdEZSVLfzGjVJTAL3COuogeCZKs2kyODECyAotLa0ENoQYdwoCBMURCgIqzDF0wiH0QsEpZJGNpLfMNEy8ZZBqwSWRDONBemoxqbBu%2bX9BrtEXJ9EGt9VVYnKx10FwVJrqoyDrqTQtqNqR9XAGvq03WTwYEyPfT%2beemnghXHohak%2fMGUs4bJ2voEqv5TaeEfEOcAbZMch%2bOy40Hg8x%2fVS%2fXkDmC%2bOajxurS781%2b3kpz%2fM%2f9N2kGO%2fhX%2fBOoF7%2fODCi3UpOWu%2bwK1UHTF%2fs136OGEt2o9SbIXuacP2jLburJzLj5WixNAMGmUpBH5%2bov7%2bHvk3
RESULTS:
I've tweaked Identity ID's, endpoint's, configurations, but Salesforce never hits our samlconsume.wgx, and all the redirect does is take me to my homepage at: https://our-company-app.salesforce.com/home/home.jsp
I'm at a loss right now. Any help would be much appreciated.

How to change idp claim value in JWT / Identity Server 4 implicit flow?

I noticed the "idp: local" in the jwt. What is the best way to change the idp claim value from "local" to "FooIDP"?
Working off of Quickstart3_ImplicitFlowAuthentication quickstart sample for Identity Server 4.
Not sure if this is the best way, but it looks like you need to change the call to SignInAsync in the AccountController::Login
Old:
await HttpContext.Authentication.SignInAsync(user.SubjectId, user.Username, props);
New:
await HttpContext.Authentication.SignInAsync(user.SubjectId, user.Username, idpName, props);

Windows Azure Active Directory Application Setup

I have created WAAD application with several reply urls, e.g.
https://localhost:4444/Search
https://server/Search
https://stage.company.com/Search
https://production.company.com/Search
I am using WSFederationAuthencationModule.CreateSignInRequest method and passing in the reply URL based upon where the code is being executed
public ActionResult Federated()
{
var module = FederatedAuthentication.WSFederationAuthenticationModule;
var scheme = Request.Url.Scheme;
var replyUrl = Url.Action("Index", "Search", null, scheme);
var requestMessage = module.CreateSignInRequest(Guid.NewGuid().ToString(), replyUrl, true);
return new RedirectResult(requestMessage.RequestUrl);
}
It seems that the code always redirects to the last Reply URL that I modified in the UI. It seems when you modify the Reply Url it is placed first in the manifest only that Reply Url is used.
Is my understanding Reply URL flawed?
I used your code in my project but wasn't able to reproduce the issue. The method that you are using produces a WSFed SSO request that contains the replyUrl encoded in the wctx parameter (pass the below SSO request produced by your code via a URL decoder and you'll see the encoded ru).
https://login.windows.net/dushyantgill.com/wsfed?wa=wsignin1.0&wtrealm=https%3a%2f%2fdushyantgill.com%2fWSFedTest&wctx=rm%3d1%26id%3d01b22db4-bfdc-4efd-abb5-2909cf445a51%26ru%3dhttps%253a%252f%252flocalhost%253a44311%252fHome%252fAbout&wct=2014-05-14T05%3a37%3a01Z
The OnAuthenticateRequest handler of the authentication module after processing the response, extracts the replyUrl from the wctx and redirects the user's agent. See http://msdn.microsoft.com/en-us/library/system.identitymodel.services.wsfederationauthenticationmodule.onauthenticaterequest(v=vs.110).aspx
Can you confirm that you have a SessionAuthenticationModule in the pipeline too.
Finally, you can always construct your owner WSFed SSO request, with an explicit WReply parameter with one of the reply URLs that you have configured with your application in AAD.
Hope this helps.
What worked for me was setting the passiveRedirectEnabled to true in the config file. Then the returnUrl parameter in the FederatedAuthentication.WSFederationAuthenticationModule.CreateSignInRequest call worked!
<wsFederation passiveRedirectEnabled="true" ....>

What are scope values for an OAuth2 server?

I'm facing a difficulty to understand how scopes work.
I found here a small text that describes the scopes of stackexchange api but i need more information on how they work (not specifically this one...). Can someone provide me a concept?
Thanks in advance
To authorize an app you need to call a URL for the OAuth2 authorization process. This URL is "living" in the API's provider documentation. For example Google has this url:
https://accounts.google.com/o/auth2/auth
Also you will need to specify a few query parameters with this link:
cliend_id
redirect_uri
scope: The data your application is requesting access to. This is typically specified as a list of space-delimited string, though Facebook uses comma-delimited strings. Valid values for the scope should be included in the API provider documentation. For Gougle Tasks, the scope is https://www.googleapis.com/auth/tasks. If an application also needed access to Google Docs, it would specify a scope value of https://www.googleapis.com/auth/tasks https://docs.google.com/feeds
response_type: code for the server-side web application flow, indivating that an authorization code will be returned to the application after the user approves the authorization request.
state: A unique value used by your application in order to prevent cross-site request forgery (CSRF) attacks on your implementation. The value should be a random unique string for this particular request, unguessable and kept secret in the client (perhaps in a server-side session)
// Generate random value for use as the 'state'. Mitigates
// risk of CSRF attacks when this value is verified against the
// value returned from the OAuth provider with the authorization
// code.
$_SESSION['state'] = rand(0,999999999);
$authorizationUrlBase = 'https://accounts.google.com/o/oauth2/auth';
$redirectUriPath = '/oauth2callback.php';
// For example only. A valid value for client_id needs to be obtained
// for your environment from the Google APIs Console at
// http://code.google.com/apis/console.
$queryParams = array(
'client_id' => '240195362.apps.googleusercontent.com',
'redirect_uri' => (isset($_SERVER['HTTPS'])?'https://':'http://') .
$_SERVER['HTTP_HOST'] . $redirectUriPath,
'scope' => 'https://www.googleapis.com/auth/tasks',
'response_type' => 'code',
'state' => $_SESSION['state'],
'approval_prompt' => 'force', // always request user consent
'access_type' => 'offline' // obtain a refresh token
);
$goToUrl = $authorizationUrlBase . '?' . http_build_query($queryParams);
// Output a webpage directing users to the $goToUrl after
// they click a "Let's Go" button
include 'access_request_template.php';
The set of query string parameters supported by the Google Authorization Server for web server applications are here:
https://developers.google.com/accounts/docs/OAuth2WebServer?hl=el#formingtheurl

Resources