Consume ProjectData from Project Server 2013 Workflow - Forbidden - workflow-foundation-4

I need to consume data from http://project/pwa/_api/ProjectData Project OData service from Project Server 2013 Workflow.
But I got "Forbidden" Response Code.
User have all rights (Administrator, Site Collection Administrator).
Consuming other endpoints (ProjectServer, Web, Lists) successfull, even from other site collections and farms.
When I need configure a security to successfully consume ProjectData?
Thank you!

Have you tried to provide credentials
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using Microsoft.SharePoint.Client;
using Microsoft.ProjectServer.Client;
static void Main(string[] args)
{
const string pwaPath = "http://ServerName/pwa/";
const string password = "pwa_password";
const string userName = "user_name#your_company.onmicrosoft.com";
var projContext = new ProjectContext(pwaPath);
var secureString = new SecureString();
password.ToCharArray().ToList().ForEach(x => secureString.AppendChar(x));
projContext.Credentials = new SharePointOnlineCredentials(userName, secureString);
// Get the list of published projects in Project Web App.
projContext.Load(projContext.Projects);
projContext.ExecuteQuery();
Console.WriteLine("\nThere are {0} projects", projContext.Projects);
}

Related

OpenID with keycloak, infinite redirect loop after successful login ASP.NET MVC 4.7

I have setup my ASP.NET MVC 4.7 application like this.
Aside from the files bello, nothing has been changed from the original generated project.
The thing is, I can successfuly redirect to my Keycloak login page, but when it redirects to the url specified after successful login, it reroutes back to the Identity server (which is keycloak) and the identity server reroutes back to the reroute URL.
Here is the dev tools log, it does look like the cookies and sessions are passed properly
After successful login in Keycloak page, it redirects to /home which is correct as that is what I set
It does looks like cookies are passed properly:
However, it does seem that after calling /home (redirect) it calls the authentication again in Keycloak
This is causing an infinite loop. As authentication will then call /home and home calls the authentication again and again.
I already tried the approaches I found in the internet including using UseKentorOwinCookieSaver, using SystemWebCookieManager, and anything I tried online with no luck.
What am I missing here? Help help, I've been stuck on this issue for days now.
Here is the code
Startup.cs
using Microsoft.Owin;
using Owin;
using System;
using System.Threading.Tasks;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Owin.Security.Keycloak;
using Microsoft.Owin.Security.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.IdentityModel.Tokens;
using Microsoft.Owin.Host.SystemWeb;
[assembly: OwinStartup(typeof(AspNetMVC4.Startup))]
namespace AspNetMVC4
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseKentorOwinCookieSaver();
const string persistentAuthType = "keycloak_auth";
app.SetDefaultSignInAsAuthenticationType(persistentAuthType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = persistentAuthType,
AuthenticationMode = AuthenticationMode.Active,
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebCookieManager()
});
var desc = new AuthenticationDescription();
desc.AuthenticationType = "keycloak_auth";
desc.Caption = "keycloak_auth";
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "Auth0",
Authority = "http://localhost:8080/auth/realms/master",
ClientId = "keycloakdemo",
ClientSecret = "tUM2gZiW5H3Lx2DQ4b5t4x5FzzrmADGi",
// RedirectUri = "http://localhost:44337/",
//PostLogoutRedirectUri = auth0PostLogoutRedirectUri,
RedirectUri = "https://localhost:44337/home",
ResponseType = OpenIdConnectResponseType.Code,
Scope = "openid profile email",
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebCookieManager(),
});
}
}
}
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace AspNetMVC4.Controllers
{
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
bool flag = User.Identity.IsAuthenticated;
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
you need to make sure you use samesite=none and also use HTTPS to get the cookies to work.
I finally figured it out and sucesfuly integrated Keycloak to ASP.NET MVC 4.7,
I am posting my solution here to help those who will have the same set of issues i had.
The thing is, Keycloak and OWIN/OpenID is not integrated seemless in ASP.NET MVC frameworks libraries so what i dis is to manualy process everything including the User Identity, process the tokens and identity and use the tokens to retrieve the informations i need thru Keycloak own Rest API.
I have made a quick and dirty demo here:
https://github.com/ruellm/ASPNetMVC4-Keycloak
Hopefuly it can help a soul someboday, as I was stuck for almost 2 weeks and finally solved it.

unable to store json in single line inside azure blob storage using azure function

I have a requirement to store json in a single line(without any formatting) inside a blob storage file. I am using azure function with Newtonsoft.JSon properties for some manipulation purpose and then writing to a blob . But when I try to using JToken.Parse I am getting exception or internal server error.Below is the code I am using:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static async Task<IActionResult> Run(HttpRequest req,TextWriter outputBlob,ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
log.LogInformation($"Response is {requestBody}");
dynamic jObject = JsonConvert.DeserializeObject(requestBody);
JToken jCategory = jObject;
var clus = jCategory["clusters"];
foreach(JObject item in clus)
{
var custom_tag=item["custom_tags"];
var app_logical_name = item.SelectToken("custom_tags.app_name");
item.SelectToken("init_scripts_safe_mode").Parent.AddAfterSelf(new JProperty("app_logical_name",app_logical_name));
}
var clus2 = JsonConvert.SerializeObject(jCategory,Formatting.None);
//var clus_new=JArray.Parse(clus).toString(Newtonsoft.Json.Formatting.None);
outputBlob.Write(clus2);
// outputBlob.Write(clus_new);
return new OkObjectResult(requestBody);
}
I have tried both ways but both are giving runtime errors. I just need to put the json in a single line(without any formatting) and write to blob.Can you please help me in this ?
This is the structure on my side:
{
"clusters":[
{
"custom_tags":{
"app_name": "appname1"
},
"init_scripts_safe_mode":{
"xxx": "yyy"
}
},
{
"custom_tags":{
"app_name": "appname2"
},
"init_scripts_safe_mode":{
"xxx2": "yyy2"
}
}
],
"test":"333"
}
And this is my code:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace FunctionApp3
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
log.LogInformation($"Response is {requestBody}");
dynamic jObject = JsonConvert.DeserializeObject(requestBody);
JToken jCategory = jObject;
var clus = jCategory["clusters"];s2 = JsonConvert.SerializeObject(jCategory, Formatting.None);
foreach (JObject item in clus)
{
var custom_tag = item["custom_tags"];
var app_logical_name = item.SelectToken("custom_tags.app_name");
var xxx = item.SelectToken("init_scripts_safe_mode");
xxx.Parent.AddAfterSelf(new JProperty("app_logical_name", app_logical_name));
log.LogInformation(JsonConvert.SerializeObject(custom_tag, Formatting.None));
log.LogInformation(JsonConvert.SerializeObject(app_logical_name, Formatting.None));
}
return new OkObjectResult(clus);
}
}
}
It seems no problem:
If you get the server side error, please check the details log to get where is the error comes from.
The 500 error is not helpful to solve this problem, you need to check the specific error of the azure function. You can use application insights to get the details error. The function must configure the corresponding application insights before you can view the log on the portal.
So you need to configure an application insights to your function app like this:
Then your function app will restart.
Of course, you can also go to kudu to view:
First, go to advanced tools, then click 'GO',
Then After you go to kudu, click Debug Console -> CMD -> LogFiles -> Application -> Functions -> yourtriggername. You will find log file there.
If you are based on linux OS, after go to kudu, just click 'log stream'(this is not supportted to consumption plan for linux.).

deliver token manually (in addition to the owin configuration way)

I have actually an asp.net website application, that can deliver token to an user with the following way :
the user logs into the application, go to a specific page and obtains a clientid and a clientsecret.
then, he calls the following api "....api/token" by giving clientid and clientsecret (client credentials grant type) to get the token.
This is the associated code :
using Microsoft.Owin;
using Owin;
using System;
using Microsoft.Owin.Security.OAuth;
[assembly: OwinStartup(typeof(MyApp.Web.App_Start.OwinStartup))]
namespace MyApp.Web.App_Start
{
public class OwinStartup
{
public void Configuration(IAppBuilder app)
{
OwinWebApiStartup.Configuration(app);
}
}
}
public static class OwinWebApiStartup
{
public static void Configuration(IAppBuilder app)
{
var provider = //my provider implementation;
var oauthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20),
Provider = provider,
};
app.UseOAuthAuthorizationServer(oauthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
AccessTokenProvider = //my provider implementation,
});
}
}
This is working nicely. But I would like to add a new feature, where a javascript client code, not an user anymore, would like to call my apis, and so it will need to have a token, but do not have a clientid and clientsecret.
This is my idea :
Create a new api endpoint, (only this one will be reachable by my javascript client code without token, and there, the code will generate a token (thanks to the username of the current user connected) and return this one (that will
be the same that an user could have obtained with the existing method) to be used by the javascript client code
I faced this problem in the past. I solved this via querystring, cause owin could only provide one token ressource. In fact it makes sense to rely on owin and on not implementing your own code.
My pseudo solution:
KeyValuePair<string, string[]> typePair = ctx.Request.Query.FirstOrDefault(x => x.Key == "type");
LoginType? loginType = GetLoginType(typePair);
[...]
switch (loginType)
{
case LoginType.User:
[...]
////within this routine you could set your claims depending on your needs
If you get another solution, I'd be grateful for sharing

Implementing ASP.Net Web Forms App with Identity Server 3 (possibly Thinktecture or not?)

I've been attempting to implement a working solution into our .Net 4.5.2 web site project. My goal is to convert my web site project, which is a web forms application (not MVC), into using identity server 3 (possibly via thinktecture's implementation) to authenticate/authorize users.
Is this possible? How will the site life cycle change or can it say the same with a session object? Can I use identity server to gain the access token and then setup a session, and only request a new access token when the session expires or the access token expires?
Some Back Story:
I do have a working identity server 3 setup at the following location https://localhost:44399. Which responds with all the appropriate calls and limitations when I use a straight html file, but when I tried to implement the same thing in the login page (portal.aspx) it seems to forget the user and does not behave the same. The website (the client) is setup on https://localhost:9898. And the login page is https://localhost:9898/portal.aspx.
I'm using the oidc-client.js on the portal.aspx page, but I keep getting CORS (cross-browser origin scripting) errors when it is trying to authenticate. Is this an issue with web forms, this does not happen when using a regular html page?
This is a .net 4.5.2 "web site project" (not a "web application project") which uses web forms, not MVC. I have got many demo MVC applications to work correctly, but when I try to take the knowledge I have to the web forms application, I see that I really don't have a grasp on what is going on.
I have added the following nuget packages:
Microsoft.Bcl v1.1.10
Microsoft.Owin v3.0.1
Microsoft.Bcl.Async v1.0.168
System.IdentityModel.Tokens.Jwt v5.1.0
Newtonsoft.Json v9.0.1
Owin v1.0.0
Microsoft.IdentityModel.Logging v1.1.0
Microsoft.Owin.Host.SystemWeb v3.0.1
System.IdentityModel.Tokens.Jwt v5.1.0
Microsoft.IdentityModel.Tokens v5.1.0
Microsoft.Bcl.Build v1.0.21
Thinktecture.IdentityModel v3.6.1
When I attempt to add the startup.cs class it tells me that these classes are typically added to the App_Code directory, and do I want to put it there? I have tried both the root and the App_Code directory and neither seem to register when they are being called. I have added the package, thinking that this is what does the call:
Microsoft.Owin.Host.SystemWeb
But it seems like the startup class is not registered. So how do I get this to register in the pipeline so that 401 not authorized errors will send the user to the identity server?
Here is my startup class:
using ExpenseTracker.WebClient.Helpers;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Newtonsoft.Json.Linq;
using Owin;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Web;
using System.Web.Helpers;
using Thinktecture.IdentityModel.Client;
[assembly: OwinStartup(typeof(ExpenseTracker.WebClient.Startup))]
namespace ExpenseTracker.WebClient
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
AntiForgeryConfig.UniqueClaimTypeIdentifier = "unique_user_key";
app.UseResourceAuthorization(new AuthorizationManager());
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "mvc",
Authority = ExpenseTrackerConstants.IdSrv,
RedirectUri = ExpenseTrackerConstants.ExpenseTrackerClient,
SignInAsAuthenticationType = "Cookies",
ResponseType = "code id_token token",
Scope = "openid profile roles expensetrackerapi offline_access",
Notifications = new OpenIdConnectAuthenticationNotifications()
{
MessageReceived = async n =>
{
EndpointAndTokenHelper.DecodeAndWrite(n.ProtocolMessage.IdToken);
EndpointAndTokenHelper.DecodeAndWrite(n.ProtocolMessage.AccessToken);
//var userInfo = await EndpointAndTokenHelper.CallUserInfoEndpoint(n.ProtocolMessage.AccessToken);
},
SecurityTokenValidated = async n =>
{
var userInfo = await EndpointAndTokenHelper.CallUserInfoEndpoint(n.ProtocolMessage.AccessToken);
// use the authorization code to get a refresh token
var tokenEndpointClient = new OAuth2Client(
new Uri(ExpenseTrackerConstants.IdSrvToken),
"mvc", "secret");
var tokenResponse = await tokenEndpointClient.RequestAuthorizationCodeAsync(
n.ProtocolMessage.Code, ExpenseTrackerConstants.ExpenseTrackerClient);
var givenNameClaim = new Claim(
Thinktecture.IdentityModel.Client.JwtClaimTypes.GivenName,
userInfo.Value<string>("given_name"));
var familyNameClaim = new Claim(
Thinktecture.IdentityModel.Client.JwtClaimTypes.FamilyName,
userInfo.Value<string>("family_name"));
var roles = userInfo.Value<JArray>("role").ToList();
var newIdentity = new ClaimsIdentity(
n.AuthenticationTicket.Identity.AuthenticationType,
Thinktecture.IdentityModel.Client.JwtClaimTypes.GivenName,
Thinktecture.IdentityModel.Client.JwtClaimTypes.Role);
newIdentity.AddClaim(givenNameClaim);
newIdentity.AddClaim(familyNameClaim);
foreach (var role in roles)
{
newIdentity.AddClaim(new Claim(
Thinktecture.IdentityModel.Client.JwtClaimTypes.Role,
role.ToString()));
}
var issuerClaim = n.AuthenticationTicket.Identity
.FindFirst(Thinktecture.IdentityModel.Client.JwtClaimTypes.Issuer);
var subjectClaim = n.AuthenticationTicket.Identity
.FindFirst(Thinktecture.IdentityModel.Client.JwtClaimTypes.Subject);
newIdentity.AddClaim(new Claim("unique_user_key",
issuerClaim.Value + "_" + subjectClaim.Value));
newIdentity.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
newIdentity.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
newIdentity.AddClaim(new Claim("expires_at",
DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
n.AuthenticationTicket = new AuthenticationTicket(
newIdentity,
n.AuthenticationTicket.Properties);
},
}
});
}
}
}
And to make things worse, adding Microsoft.Owin.Host.SystemWeb does not compile due to strange errors like:
The type or namespace name 'SessionState' does not exist in the
namespace 'System.Web' (are you missing an assembly reference?)

WSFederation Sign-in - Asp.net 4.6.1

So I'm trying to sort out web-based authentication using the WSFederation protocal. We've sorted out the setup, and my web app can reach the login page, after some headache:
(Asp.net on-premises authentication - The remote certificate is invalid according to the validation procedure)
Now I'm getting a 'IDX10201: None of the the SecurityTokenHandlers could read the 'securityToken' error. From what I understand, we'll need middleware to deal with the security tokens. So I'm trying to get started with this:
https://www.scottbrady91.com/Katana/WS-Federation-Token-Encryption-using-Microsoft-Katana
So I've set the TokenValidationParameters option in the WsFederationAuthenticationOptions, but I'm getting an error from VisualStudio saying that 'Cert' does not exist in the current context. I'm confused as to why, as my code is nearly identical to the guides.
I'm also wondering if our certificate has simply been improperly configured. I came across some SSL guidelines for ADFS, and I know our IT guy hasn't gone down that road (yet). I'd like to rule that out as a possible cause, but if someone knows that it is, or is not, the cause, it'd save me time and be greatly appreciated.
EDIT: After some reading, there are some things that are unclear to me? We're using an ADFS server to handle the credentials, but as I understand it, ADFS should also handle our tokens without any additional work. Am I wrong? Should I be using middleware? Or is there a problem with the ADFS server configuration?
using System;
using System.Collections.Generic;
using System.Configuration;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.WsFederation;
using Owin;
using System.Security.Cryptography.X509Certificates;
using System.Security;
using System.Net.Security;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.IdentityModel.Tokens;
using Microsoft.Owin;
using RCHHRATool;
using System.Net;
using System.IdentityModel.Selectors;
namespace RCHHRATool
{
public partial class Startup
{
private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"];
private X509Certificate2 certificate;
public void ConfigureAuth(IAppBuilder app)
{
Debug.WriteLine("Configure Auth Started");
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType =
WsFederationAuthenticationDefaults.AuthenticationType });
//System.Net.ServicePointManager.ServerCertificateValidationCallback.
//ServicePointManager.ServerCertificateValidationCallback = RemoteCertificateValidationCB;
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach(X509Certificate2 cert in store.Certificates)
{
Debug.WriteLine(cert.Issuer);
if (cert.Issuer.Equals("CN=xxxxx.xxxxx.com"))
{
this.certificate = new X509Certificate2(cert);
}
}
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
SignInAsAuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
Wtrealm = "https://localhost:44340",
Wreply = "http://localhost:56879",
MetadataAddress = adfsMetadata,
AuthenticationType = "adfs",
SecurityTokenHandlers = new SecurityTokenHandlerCollection
{
new X509SecurityTokenHandler
{
Configuration = new SecurityTokenHandlerConfiguration
{
IssuerTokenResolver = new X509CertificateStoreTokenResolver(StoreName.Root,
StoreLocation.LocalMachine)
}
}
}
//},
//TokenValidationParameters = new TokenValidationParameters
//{
// ValidAudience = "https://localhost:44340/",
// ValidIssuer = "xxxxx.xxxxx.com",
// IssuerSigningToken = new X509SecurityToken(this.certificate)
//}
});
}
Turns out that asp.net (framework 4.6.1) & ws-federation doesn't handle encrypted security tokens out of the box. I followed a great guide to resolve the token error. After some tuning (watch your certificate footprint, and make sure your certificates are in trusted root), I managed to get the authentication working.

Resources