How to add RegEx validations in Azure API Management Policy Expression - regex

I need to add a RegEx validation for email in my Azure API Management Policy expression but there is no proper documentation available.
Is it possible to have RegEx validation in Azure APIM?

This can be done by setting a variable with C# regex code.
The variable can be used in the choose policy for further processing
Complete policy return result of regex match:
<policies>
<inbound>
<base />
<set-variable name="isEMailValid" value="#{
var pattern = #"^((([!#$%&'*+\-/=?^_`{|}~\w])|((?!.*\.\.)[!#$%&'*+\-/=?^_`{|}~\w][!#$%&'*+\-/=?^_`{|}~\.\w]{0,}[!#$%&'*+\-/=?^_`{|}~\w]))[#]\w+([-.]\w+)*\.\w+([-.]\w+)*)$";
var body = (JObject)context.Request.Body.As<JObject>(true);
var regex = new Regex(pattern);
if(regex.Match(body["email"].Value<string>()).Success)
{
return true;
}
return false;
}" />
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
var response = new JObject();
response["isEMailValid"] = context.Variables.GetValueOrDefault<bool>("isEMailValid");
return response.ToString();
}</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Request valid:
POST https://rfqapiservicey27itmeb4cf7q.azure-api.net/sample/ipsum HTTP/1.1
Host: rfqapiservicey27itmeb4cf7q.azure-api.net
{ "email": "a#b.com"}
Response valid:
HTTP/1.1 200 OK
content-length: 28
content-type: application/json
date: Fri, 03 Feb 2023 07:15:32 GMT
vary: Origin
{
"isEMailValid": true
}
Request invalid:

Related

Cognito logout does not work as documented

I have a Cognito user pool configured with a SAML identity provider (ADFS) and I'm able to sign it as a federated user (AD) but sign out does not work.
Following the documentation, I make a GET request to
https://my-domain.auth.us-west-2.amazoncognito.com/logout?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com (using some public logout uri), from my client (an AngularJS 1.x app), and I get back a 302 with a Location header like
https://my-domain.auth.us-west-2.amazoncognito.com/login?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com
(In fact there I see 2 requests like the above).
When I log back in (thru ADFS) it does not prompt for my AD credentials, i.e. seems that I'm not logged out.
My user pool is configured as described here (see step 7), where the Enable IdP sign out flow is checked, which is supposed to log the user out from ADFS as well.
Any suggestions?
Thanks.
General
-------
Request URL: https://my-domain.auth.us-west-2.amazoncognito.com/logout?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com
Request Method: GET
Status Code: 302
Remote Address: 54.69.30.36:443
Referrer Policy: no-referrer-when-downgrade
Response Headers
----------------
cache-control: private
content-length: 0
date: Fri, 20 Apr 2018 21:31:12 GMT
expires: Thu, 01 Jan 1970 00:00:00 UTC
location: https://my-domain.auth.us-west-2.amazoncognito.com/login?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com
server: Server
set-cookie: XSRF-TOKEN=...; Path=/; Secure; HttpOnly
set-cookie: XSRF-TOKEN=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
status: 302
strict-transport-security: max-age=31536000 ; includeSubDomains
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block
Request Headers
---------------
:authority: my-domain.auth.us-west-2.amazoncognito.com
:method: GET
:path: /logout?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
authorization: Bearer eyJra...
cache-control: no-cache
origin: https://localhost:8443
pragma: no-cache
referer: https://localhost:8443/logout
user-agent: Mozilla/5.0...
This redirect happens whenever logout_uri parameter doesn't match exactly what's listed among Sign out URL(s) in AWS Cognito User Pools App client settings configuration.
Cognito allows logout with either logout_uri or with the same arguments as login (i.e. redirect_uri and response_type) to log out and take the user back to the login screen. It seems that whenever logout_uri is invalid, it assume the re-login flow, does this redirect, and then reports an error about missing login arguments.
As for SAML, I don't know, but guessing that it doesn't work because there was actually an error, just not properly reported.
The /logout endpoint signs the user out.It only supports HTTPS GET. It is working
Sample Requests - Logout and Redirect Back to Client
It clears out the existing session and redirects back to the client. Both parameters are required.
GET https://<YOUR DOMAIN NAME>/logout?
client_id=xxxxxxxxxxxx&
logout_uri=com.myclientapp://myclient/logout
Also make sure that Logout URL is same as SIGNOUT URL in AWS Cognito APP too.
for more information, refer AWS LOGOUT Endpoint
Finally I was able to fix this issue. I found the actual cause of the issue from the event viewer of my windows server 2012 R2. It says the following details about the failed sign out flow.
The SAML Single Logout request does not correspond to the logged-in session participant.
Requestor: urn:amazon:cognito:sp:userpoolid
Request name identifier: Format: urn:oasis:names:tc:SAML:2.0:nameid-format:persistent, NameQualifier: SPNameQualifier: , SPProvidedId:
Logged-in session participants:
Count: 1, [Issuer: urn:amazon:cognito:sp:userpoolid, NameID: (Format: , NameQualifier: SPNameQualifier: , SPProvidedId: )]
User Action
Verify that the claim provider trust or the relying party trust configuration is up to date. If the name identifier in the request is different from the name identifier in the session only by NameQualifier or SPNameQualifier, check and correct the name identifier policy issuance rule using the AD FS 2.0 Management snap-in.
The Error clearly says that the name identifier in the request is different from the name identifier in the session only by NameQualifier. I have corrected this error in the claim issuance tab of relying party trusts by adding the rule as below. The below rule replace the user#myadfsdomain to simply user when issuing the claim.
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = RegExReplace(c.Value, "(?i)^myadfsdomain*\\", ""), ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent");
Besides this i have forgot to check in the enable signout flow in the cognito configuration which caused the problem. Logout started working seamlessly for me.
From documentation here
https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html
If you want
And your login url is like
"https://xxxx.auth.eu-west-1.amazoncognito.com/login?client_id=1234&response_type=token&scope=aws.cognito.signin.user.admin+email+openid+phone+profile&redirect_uri=http://localhost:3000/"
Then your logout url is like
"https://xxxx.auth.eu-west-1.amazoncognito.com/logout?client_id=1234&logout_uri=http://localhost:3000/";
Note the difference
login > login? and redirect_uri
logout > logout? and logout_uri
Those redirect / logout uri must match what you have configured inside Cognito.
If you don't do it right, you may get strange errors like
error=unauthorized_client
or
Required String parameter 'response_type' is not present
and who knows what else. :o)
My solution below is based on -
https://github.com/nextauthjs/next-auth/discussions/3938#discussioncomment-2165155
I am able to solve the issue with few changes-
in signout button upon clicking i will call handleSignOut
function handleSignOut() {
const clientId = 'paste your AWS COGNITO CLIENT ID';
const domain = 'paste your AWS COGNITO DOMAIN';
window.location.href = `${domain}/logout?client_id=${clientId}&logout_uri=http://localhost:3000/logout`
}
In Aws cognito-> app integration -> app client settings -> signout url
keep the following url-
http://localhost:3000/logout
in pages create a seperate page called logout and keep following code-
import { useEffect } from "react"
import { signOut } from 'next-auth/react'
export default function SignoutPage() {
useEffect(() => {
setTimeout(() => {
signOut({ callbackUrl: window.location.origin });
}, 1000);
}, []);
return (
<>
<div className="loader">
Loading..
</div>
<style jsx>{`
.loader{
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transform: -webkit-translate(-50%, -50%);
transform: -moz-translate(-50%, -50%);
transform: -ms-translate(-50%, -50%);
}
`}</style>
</>
)
}
click signout it will log you out
#RequestMapping(value = "/logout", method = RequestMethod.GET)
public void logout(HttpServletRequest request, HttpServletResponse response) {
LOGGER.info("Logout controller");
try {
Cookie awsCookie = new Cookie("AWSELBAuthSessionCookie-0", "deleted");
awsCookie.setMaxAge(-1);
awsCookie.setPath("/");
response.addCookie(awsCookie);
Properties appProperties = new Properties();
applicationPropertyConfigurer.loadProperties(appProperties);
response.sendRedirect(appProperties.getProperty("logout.url"));
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", -1);
request.getSession().invalidate();
} catch (IOException e) {
LOGGER.error("Exception in redirecting to logout.url URL", e);
}
}
//https:/domain_name.auth.us-west-x.amazoncognito.com/logout?response_type=code&client_id=&redirect_uri=redirect_uri_should_be_present_in_cognito_user_pool_Callback URL&state=STATE&scope=openid

IdentityServer multi IDP configuration request

How do I create a request that uses the Saml or Google IDP for authorization? My understanding is that if I put "acr_values=idp:Google" in my request then the request will be routed through my Google Idp path. Likewise if I put "acr_values=idp:saml2p" it should go through my Saml Idp. Is it a different path? Right now the request goes to localhost:98575/connect/token.
Here is my current Request:
POST /connect/token HTTP/1.1
Host: localhost:98575
Authorization: Basic SomethingEncrypted
Cache-Control: no-cache
Postman-Token: AGuid
Content-Type: application/x-www-form-urlencoded
grant_type=password&scope=customScope+openid+offline_access&username=actuallyfoobar#enterprisebeta.com&password=SomePassword&acr_values=idp:Google
Here is a snippet from my IdentityServer Starup.cs file:
public static void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
{
var authServicesOptions = new KentorAuthServicesAuthenticationOptions(false)
{
SPOptions = new SPOptions
{
EntityId = new EntityId("http://sp.example.com")
},
SignInAsAuthenticationType = signInAsType,
AuthenticationType = "saml2p",
Caption = "SAML2p",
};
authServicesOptions.IdentityProviders.Add(new IdentityProvider(
new EntityId("http://stubidp.kentor.se/Metadata"),
authServicesOptions.SPOptions)
{
LoadMetadata = true,
});
app.UseKentorAuthServicesAuthentication(authServicesOptions);
var google = new GoogleOAuth2AuthenticationOptions
{
AuthenticationType = "Google",
Caption = "Google",
SignInAsAuthenticationType = signInAsType,
ClientId = "767400843187-8boio83mb57ruogr9af9ut09fkg56b27.apps.googleusercontent.com",
ClientSecret = "5fWcBT0udKY7_b6E3gEiJlze"
};
app.UseGoogleAuthentication(google);
The acr_values with "idp:foo" is only used for the authorization endpoint, not the token endpoint. IOW, it's only used to automatically redirect the user in the browser to the indicated external identity provider.

query a webpage for variable entity using a consistent URL structure

Would someone please help me to understand how I might inject into my program a query to this webpage?
There are two parameters that need to be set, i.e.
"Site:", is where you enter the language and site code.
&
"Page:", you must put in the exact title of the page as it appears on the connected site.
The URL's always look like this:
https://www.wikidata.org/wiki/Special:ItemByTitle?site=en&page=Mikhail+Bakunin&submit=Search
https://www.wikidata.org/wiki/Special:ItemByTitle?site=en&page=Thomas+Edward+Lawrence&submit=Search
and the language is always English, so you see, it's just:
https://www.wikidata.org/wiki/Special:ItemByTitle?site=en&page=Blah+Blah&submit=Search
The objective of querying that page is to retrieve the ID value associated with the page, so for Mikhail Bakunin it's Q27645 and for T. E. Lawrence it's Q170596
It becomes part of the URL once the page is reached:
https://www.wikidata.org/w/index.php?title=Q170596&site=en&page=Thomas+Edward+Lawrence&submit=Search
But also maybe I could strip it from the page, using beautifulSoup or soemthng?(that's a guess)
The program needs to be generalizable, which is to say, that the name of the entity we're searching for is variable, it will change in the program, so that needs to be taken in account.
I guess using python or php or something would not be a crime against humanity if it's easier, though I prefer java.
update:
import java.net.*;
import java.io.*;
public class URLConnectionReader
{
public static void main(String[] args) throws Exception
{
URL site = new URL("https://www.wikidata.org/wiki/Special:ItemByTitle?site=en&page=Mikhail+Bakunin&submit=Search");
URLConnection yc = site.openConnection();
BufferedReader in = new BufferedReader(
new InputStreamReader(
yc.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
this works sort of, but the result is quite messy.
I guess I could grab it out of this thing:
<!-- wikibase-toolbar --><span class="wikibase-toolbar-container"><span class="wikibase-toolbar-item wikibase-toolbar ">[<span class="wikibase-toolbar-item wikibase-toolbar-button wikibase-toolbar-button-edit">edit</span>]</span></span>
but how?
When you request this URL the response is:
HTTP/1.1 302 forced.302
Server: Apache
X-Powered-By: HHVM/3.3.1
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Vary: Accept-Encoding,X-Forwarded-Proto,Cookie
X-Content-Type-Options: nosniff
Location: http://www.wikidata.org/w/index.php?title=Q27645&site=en&page=Mikhail+Bakunin&submit=Search
Content-Type: text/html; charset=utf-8
X-Varnish: 1641959068, 1690824779, 1606045625
Via: 1.1 varnish, 1.1 varnish, 1.1 varnish
Transfer-Encoding: chunked
Date: Fri, 17 Apr 2015 11:49:55 GMT
Age: 0
Connection: keep-alive
X-Cache: cp1054 miss (0), cp3003 miss (0), cp3013 frontend miss (0)
Cache-Control: private, s-maxage=0, max-age=0, must-revalidate
Set-Cookie: GeoIP=NL:XXX:51.4400:5.6194:v4; Path=/; Domain=.wikidata.org
So there's a 302 redirect in the HTTP response headers. That's where you'll want to grab your Q-number. Simlpy regex it out of the Location header with a regex like:
^Location:.*?title=(Q[0-9]+)
...and use matchgroup 1 (should be Q27645).
To grab the HTTP headers, have a look at this page; it basically goes like:
URL obj = new URL("https://www.wikidata.org/wiki/Special:ItemByTitle?site=en&page=Mikhail%20Bakunin&submit=Search");
URLConnection conn = obj.openConnection();
//get header by 'key'
String location = conn.getHeaderField("Location");
//TODO: Regex here

Owin cookie authentication set-cookie not saving in browser

I am building self-hosted web server on this stack:
OWIN
Nancy
Web Api 2
And I am using Microsoft.Owin.Security.Cookies from Katana for forms-like authentication. I got Set-Cookie header in response, but cookie don't being saved and not being included in next request. So what's the problem? What I am doing wrong?
Owin startup:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = "GM",
CookieHttpOnly = true,
CookieSecure = CookieSecureOption.SameAsRequest,
CookiePath = "/",
CookieName = CookieAuthenticationDefaults.CookiePrefix + "GM",
CookieDomain = "localhost",
});
Controller code:
var context = Request.GetOwinContext();
context.Authentication.SignIn(new AuthenticationProperties()
{
IsPersistent = true
},
new ClaimsIdentity(new[] {new Claim(ClaimsIdentity.DefaultNameClaimType, user.Login)}, "GM"));
context.Response.Headers.Add("Location", new []{ "/" });
return Request.CreateResponse(HttpStatusCode.Found);
Response headers:
Cache-Control:no-cache
Content-Length:0
Date:Wed, 11 Sep 2013 11:11:23 GMT
Expires:-1
Location:/
Pragma:no-cache
Server:Microsoft-HTTPAPI/2.0
Set-Cookie:.AspNet.GM=AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAABui2rBibE0yPXB0-v3C06gAAAAACAAAAAAAQZgAAAAEAACAAAAC1mQV3jGo_WAhMQ-hzsmzgkdbdCclWIAX-msbE0_12zQAAAAAOgAAAAAIAACAAAABuQjBg3EJIka151hvBgtlPGfQ2O_cwNI2VVh86dchTDXAAAAD21O9DnNk4yLU9eddVfY3bT9P1CEudNeLvwohkSTAQBP2onuIQfgl9F99Je5waPddckh2llD2kjftSMQPhzgE9vKm-_wE42hXhc9FIgfxpD5AdaeGatwpEcwDfGJJdpQnObX1pbjEFIXLVJxGm5qMUQAAAAC8AiFTaXmzrfRy4-jR6zqMmSKddzddmiBLGClAckWOy6W2YWdf50N2zhIj_MwN8-zi-B0tlv87pzAt-6RDZYZs; domain=localhost; path=/; expires=Wed, 25-Sep-2013 11:11:24 GMT; HttpOnly
I resolved the issue. It was because 'domain=localhost'. It seems to 'localhost' isn't valid value for domain parameter.
You could be encounting a similar issue to: Safari doesn't set Cookie but IE / FF does
Try setting the HttpStatusCode to HttpStatusCode.Ok
Some browsers will only accept cookies from a certain response code (like 200, 302).

WebService non-standard Authorization Type

I have a service provider that requires a non-standard Authorization Type for their web service.
Instead of Basic, Digest, NTLM, etc., they require "Basic API Access.".
I have tried the code below and used Fiddler to check the web service request.
It does not seem that any custom strings can be used
Binding avail = new Binding();
avail.Url = "https://amer.webservicesxx.com/Availability";
NetworkCredential netCredential = new NetworkCredential("abc", "def");
Uri uri = new Uri(avail.Url);
CredentialCache cred = new CredentialCache();
cred.Add(uri, "Basic API Access", netCredential);
avail.Credentials = cred;
SearchRsp response = avail.service(request);
POST https://amer.webservicesxx.com/Availability HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client
Protocol 4.0.30319.1) VsDebuggerCausalityData:
uIDPo+CJPwpspBxKp939c... Content-Type: text/xml; charset=utf-8
SOAPAction: "" Host: america.webservicesxx.com Content-Length:
658 Expect: 100-continue
?xml version="1.0" encoding="utf-8"?> ?soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/20 01/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"> ...
?/soap:Envelope>
It is missing the ...
Authorization: Basic API Access/dUFQSS04NDA0NTk2MDk6WiFyYzBuZDg0...
...line in the header.
If I just use "Basic" it adds the Authorization line properly, but is rejected by the service provider.
Is this the correct syntax to add an Authorization: header to the Soap transaction?
I needed to replace data in the web request.
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
WebRequest request = base.GetWebRequest(uri);
request.Headers.Add("Authorization: Basic API Access dUFQSS04NDA0NT...");
return request;
}
inside the web service reference class.