Pass a token via the addition of a URL query parameter with "token=tokenvalue." in Azure Media Service not working - azure-media-services

Trying to stream a video from Azure Media Service into a Xamarin.Form application.
The asset is protected with a JWT token.
I use the following code to generate the token:
private string GetJWT(string PrimaryKey) {
var tokenSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(PrimaryKey));
SigningCredentials cred = new SigningCredentials(tokenSigningKey, SecurityAlgorithms.HmacSha256);
JwtSecurityToken token = new JwtSecurityToken(
issuer: "xxx",
audience: "yyy",
claims: null,
notBefore: DateTime.Now.AddMinutes(-5),
expires: DateTime.Now.AddMinutes(60),
signingCredentials: cred);
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
return handler.WriteToken(token);
}
The URI for the asset is like : https://xxx.streaming.media.azure.net/12222-1565-232323-a5b8-c10e148273ae/Test.ism/manifest(format=m3u8-cmaf,encryption=cbc)
If I use Azure Media Player (https://ampdemo.azureedge.net) to test the URI and the AES token, it works fine. So I guess there is no problem with the token itself...
The documentation (https://learn.microsoft.com/en-us/azure/media-services/latest/security-pass-authentication-tokens-how-to#pass-a-token-via-the-addition-of-a-url-query-parameter-with-tokentokenvalue) says that the following code should work to send the token directly with the url.
I need to do this as with Xamarin.Forms and MediaElement, I can't send the token in a header request. So I need the querystring option...
string armoredAuthToken = System.Web.HttpUtility.UrlEncode(authToken);
string uriWithTokenParameter = string.Format("{0}&token={1}", keyDeliveryServiceUri.AbsoluteUri, armoredAuthToken);
Uri keyDeliveryUrlWithTokenParameter = new Uri(uriWithTokenParameter);
player.Source = keyDeliveryUrlWithTokenParameter; (player is a MediaElement control)
But the video is never loaded.
In my opinion there is an error, it should be {0}?token={1} instead of {0}&token={1}.
But that doesn't work neither.
If I test with VLC the https://xxx.streaming.media.azure.net/12222-1565-232323-a5b8-c10e148273ae/Test.ism/manifest(format=m3u8-cmaf,encryption=cbc)?token=zzzzz, it doesn't work neither.
I presume there is a problem with token in the querystring, as if Azure can't read it.

Related

How to call SSRS Rest-Api V1.0 with custom security implemented (NOT SOAP)

I have implemented the custom security on my reporting services 2016 and it displays the login page once the URL for reporting services is typed on browser URL bar (either reports or reportserver)
I am using the following code to pass the Credentials
when i use the code WITHOUT my security extension it works and looks like this
ICredentials _executionCredentials;
CredentialCache myCache = new CredentialCache();
Uri reportServerUri = new Uri(ReportServerUrl);
myCache.Add(new Uri(reportServerUri.GetLeftPart(UriPartial.Authority)),
"NTLM", new NetworkCredential(MyUserName, MyUserPassword));
_executionCredentials = myCache;
when i use the code WITH the security extension it doesnt work and looks like this
ICredentials _executionCredentials;
CredentialCache myCache = new CredentialCache();
Uri reportServerUri = new Uri(ReportServerUrl);
myCache.Add(new Uri(reportServerUri.GetLeftPart(UriPartial.Authority)),
"Basic", new NetworkCredential(MyUserName, MyUserPassword));
_executionCredentials = myCache;
and i get an Exception saying "The response to this POST request did not contain a 'location' header. That is not supported by this client." when i actually use this credentials
Is "basic" the wrong option ?
Have anyone done this ?
Update 1
Well it turns out that my SSRS is expecting an Authorisation cookie
which i am unable to pass (according to fiddler, there is no cookie)
HttpWebRequest request;
request = (HttpWebRequest)HttpWebRequest.Create("http://mylocalcomputerwithRS/Reports_SQL2016/api/v1.0");
CookieContainer cookieJar = new CookieContainer();
request.CookieContainer = cookieJar;
Cookie authCookie = new Cookie("sqlAuthCookie", "username:password");
authCookie.Domain = ".mydomain.mylocalcomputerwithRS";
if (authCookie != null)
request.CookieContainer.Add(authCookie);
request.Timeout = -1;
HttpWebResponse myHttpWebResponse = (HttpWebResponse)request.GetResponse();
That's how I got it (SSRS 2017; api v2.0). I took the value for the "body" from Fiddler:
var handler = new HttpClientHandler();
var httpClient = new HttpClient(handler);
Assert.AreEqual(0, handler.CookieContainer.Count);
// Create a login form
var body = new Dictionary<string, string>()
{
{"__VIEWSTATE", "9cZYKBmLKR3EbLhJvaf1JI7LZ4cc0244Hpcpzt/2MsDy+ccwNaw9hswvzwepb4InPxvrgR0FJ/TpZWbLZGNEIuD/dmmqy0qXNm5/6VMn9eV+SBbdAhSupsEhmbuTTrg7sjtRig==" },
{"__VIEWSTATEGENERATOR", "480DEEB3"},
{ "__EVENTVALIDATION", "IS0IRlkvSTMCa7SfuB/lrh9f5TpFSB2wpqBZGzpoT/aKGsI5zSjooNO9QvxIh+QIvcbPFDOqTD7R0VDOH8CWkX4T4Fs29e6IL92qPik3euu5QpidxJB14t/WSqBywIMEWXy6lfVTsTWAkkMJRX8DX7OwIhSWZAEbWZUyJRSpXZK5k74jl4x85OZJ19hyfE9qwatskQ=="},
{"txtUserName", "User"},
{"txtPassword", "1"},
{"btnLogin","Войти"}
};
var content = new FormUrlEncodedContent(body);
// POST to login form
var response = await httpClient.PostAsync("http://127.0.0.1:777/ReportServer/Logon.aspx", content);
// Check the cookies created by server
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
var cookies = handler.CookieContainer.GetCookies(new Uri("http://127.0.0.1:777/ReportServer"));
Assert.AreEqual("sqlAuthCookie", cookies[0].Name);
// Make new request to secured resource
var myresponse = await httpClient.GetAsync("http://127.0.0.1:777/Reports/api/v2.0/Folders");
var stringContent = await myresponse.Content.ReadAsStringAsync();
Console.Write(stringContent);
As an alternative you can customize SSRS Custom Security Sample quite a bit.
I forked Microsoft's Custom Security Sample to do just what you are describing (needed the functionality at a client long ago and reimplemented as a shareable project on GitHub).
https://github.com/sonrai-LLC/ExtRSAuth
I created a YouTube walkthrough as well to show how one can extend and debug SSRS security with this ExtRSAuth SSRS security assembly https://www.youtube.com/watch?v=tnsWChwW7lA
TL; DR; just bypass the Microsoft example auth check in Login.aspx.cs and put your auth in Page_Load() or Page_Init() event of Login.aspx.cs- wherever you want to perform some custom logging check- and then immediately redirect auth'd user to their requested URI.

IdentityServer3 Guidance Needed

I have a scenario where a client has an OpenIdConnect (OIDC) token in their possession. The OIDC was issued from an external OIDC provider, I am not the OIDC provider, just the downstream consumer of it.
The goal is for the client to exchange said OIDC Token, for temporary credentials, or an accesstoken, which will then give them api access to more specific resources.
In my case, the OIDC represents a user. The client, has a ClientId/Secret, which is used to establish service-2-service trust. In the end I would like to have something that looks a lot like the CustomGrant token Request.
static TokenResponse GetCustomGrantToken()
{
var client = new TokenClient(
token_endpoint,
"custom_grant_client",
"cd19ac6f-3bfa-4577-9579-da32fd15788a");
var customParams = new Dictionary<string, string>
{
{ "some_custom_parameter", "some_value" }
};
var result = client.RequestCustomGrantAsync("custom", "read", customParams).Result;
return result;
}
where my customParams would contain the OIDC to my user.
Problem: I can get a token back from the GetCustomGrantToken call, however a follow up Webapi call fails to pass Authorization. i.e. Identity.isAuthenticated is false.
The it all works fine if I get a clientcredential token.
static TokenResponse GetClientToken()
{
var client = new TokenClient(
token_endpoint,
"silicon",
"F621F470-9731-4A25-80EF-67A6F7C5F4B8");
return client.RequestClientCredentialsAsync("api1").Result;
}
Had the CustomGrantToken worked I would have put my users account info in the claims, thus giving me context in the subsequent WebApi calls.
Any direction would be appreciated.

Payflow Gateway w/ Secure Token & Transparent Redirect - return URL issue

I've built a client (in .NET, but it could be in any framework) to consume the Payflow Gateway NVP API using the Transparent Redirect and Secure Token features. I am able to receive the token, send the credit card data, and receive an Approved response from PayPal. The problem is that PayPal is not redirecting properly back to my site. I passed a RETURNURL (http://localhost:49881/transaction/details?processor=PayflowGateway) parameter when requesting the Secure Token, but instead of returning me to that URL after the transaction, it navigates my browser to the following URL:
https://pilot-payflowlink.paypal.com/http%3A%2F%2Flocalhost%3A49881%2Ftransaction%2Fdetails%3Fprocessor%3DPayflowGateway?POSTFPSMSG=No%20Rules%20Triggered&RESPMSG=Approved&ACCT=1111&COUNTRY=US&PROCCVV2=M&VISACARDLEVEL=12&CVV2MATCH=Y&CARDTYPE=0&PNREF=A70A8EB8B6A1&AVSDATA=XXN&SECURETOKEN=9eGKZsSldEU6mIdSEV5DB4wWd&PREFPSMSG=No%20Rules%20Triggered&SHIPTOCOUNTRY=US&AMT=14.75&SECURETOKENID=1850a8f2-f180-4474-aa31-35d736fd7921&TRANSTIME=2016-03-24%2007:58:48&HOSTCODE=A&COUNTRYTOSHIP=US&RESULT=0&BILLTOCOUNTRY=US&AUTHCODE=872PNI&EXPDATE=1218
I have tried removing the "?processor=PayflowGateway" to fix the multiple question mark issue in the URL, but that doesn't seem to help. I've also tried tagging the RETURNURL[xx] with xx being the length of the URL value, but that seems to be the same as not passing a RETURNURL at all as it just shows a confirmation page on paypal.com instead of redirecting back to my site.
In PayPal Manager, I set the "Show confirmation page" setting to "On my website", Return URL to blank, and Return URL Method to GET. Are there any other settings or API request changes I need to make to get this to return properly to my test site?
This problem is caused because you're URL-Encoding the RETURNURL parameter passed when requesting the secure token from payflowpro gateway.
See the Do Not URL Encode Name-Value Parameter Data section on the Integration Guide.
Also, here you can get some C# code working you can use.
And some guidelines about PayPal HTTP here.
Do not use System.Net.Http.HttpClient nor System.Net.WebClient to make the HTTP POST to request the secure token. Instead use the low level System.Net.WebRequest to be able to write the POST data unencoded.
For example:
private string RequestSecureToken(double amount)
{
var secureTokenId = Guid.NewGuid().ToString();
var requestId = Guid.NewGuid().ToString();
var pairs = new Dictionary<string, string>()
{
{"PARTNER", "PayPal"},
{"VENDOR", "VENDOR NAME"},
{"USER", "USER NAME"},
{"PWD", "PASSWORD"},
{"TRXTYPE", "S"},
{"AMT", amount.ToString()},
{"CREATESECURETOKEN", "Y"},
{"SECURETOKENID", secureTokenId},
{"SILENTTRAN", "TRUE"},
{"RETURNURL", "http://mycompany.com/success"},
{"ERRORURL", "http://mycompany.com/error"}
};
string postData = string.Join("&", pairs.Select(p => string.Format("{0}[{2}]={1}", p.Key, p.Value, p.Value.Length)));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://pilot-payflowpro.paypal.com");
request.Method = "POST";
request.ContentType = "text/namevalue";
request.Headers.Add("X-VPS-CLIENT-TIMEOUT", "45");
request.Headers.Add("X-VPS-REQUEST-ID", requestId);
request.ContentLength = postData.Length;
using (var writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(postData);
}
//Get the response
var response = request.GetResponse();
using (var reader = new StreamReader(response.GetResponseStream()))
{
return reader.ReadToEnd();
}
}

ADAL - ClientAssertionCertificate

We can successfully acquire a token using the following code:
var certificate = Certificate.Load("Client.pfx", "notasecret");
var authenticationContext = new AuthenticationContext(authority);
var clientAssertionCertificate = new ClientAssertionCertificate(clientId, certificate);
return await authenticationContext.AcquireTokenAsync(resource, clientAssertionCertificate);
The token doesnt seem to contain any information that we can use to identity the client. In our use case we have lots of daemon service clients that communicate to a API. We need to have some unique identified available on the server.
I also tried creating our own JWT token and added some public claims, such as name. However after requesting client assertion type using the following code fragment
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "clientid", clientId },
{ "resource", resource },
{ "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" },
{ "grant_type", "client_credentials" },
{ "client_assertion", jwt }
});
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://login.windows.net/{guid}/")
};
var response = await httpClient.PostAsync("oauth2/token", content);
The return token had none of my custom information.
Question: Is there a way to pass custom claims using ClientAssertionCertificate flow? where the token returned has additional information.
There is currently no way of adding custom claims in tokens issued for applications.
The token you receive should contain the claims appid (which identifies the client_id of the application who requested the token) and tid (which indicates which azure AD tenant the app is operating on). Those two should be enough for you to identify the calling application. Now, if rather than the application you want to identify the process (as in, instance of application X running on server A and instance of application X running on server B) then I don't believe we have anything in Azure AD today that would help you to tell the two apart - for Azure AD if they have the same client_id and secret, they are the same application.

Authentication for Azure REST Api

I want to communicate with Azure only with REST Api and without any sdk component - communicate to the new ARM
how can I get the Bearer token for the authentication? i saw that all the examples use the SDK for that
To get a token for authorization, you need to install the Active Directory Authentication Library into your project. The easiest way to do this is to use the NuGet package.
Use this code to get the token:
public static string GetAToken()
{
var authenticationContext = new AuthenticationContext("https://login.windows.net/{tenantId or tenant name}");
var credential = new ClientCredential(clientId: "{application id}", clientSecret: {application password}");
var result = authenticationContext.AcquireToken(resource: "https://management.core.windows.net/", clientCredential:credential);
if (result == null) {
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
There are more details and code examples at Authenticating Azure Resource Manager requests.