OIDCClient LogoutAsync throwing SemaphoreFullException - wpf

I'm connecting to Identity Server 4 from a WPF application and I am trying to perform a logout. However, I get a SemaphoreFullException in my WpfEmbeddedBrowser when calling _oidcClient.LogoutAsync(). Initally I thought maybe I shouldn't use the browser for logging out, but I don't think that is possible.
Here is the part calling the login and logout:
//prompt login
var options = new OidcClientOptions()
{
Authority = Current.Properties["IdentityServerAPIAddress"].ToString(),
ClientId = "wpf",
ClientSecret = "secret",
Scope = "openid offline_access WebAPI",
RedirectUri = "http://localhost/signin-oidc",
Flow = OidcClientOptions.AuthenticationFlow.AuthorizationCode,
Browser = new WpfEmbeddedBrowser()
};
_oidcClient = new OidcClient(options);
try
{
loginResult = await _oidcClient.LoginAsync();
logoutResult = await _oidcClient.LogoutAsync();
}
...
Here is the WpfEmbeddedBrowser:
private BrowserOptions _options = null;
public WpfEmbeddedBrowser()
{
}
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
_options = options;
var window = new Window()
{
Width = 450,
Height = 750,
Title = "SiteMonitor Desktop Application Login"
};
var webBrowser = new WebBrowser();
var signal = new SemaphoreSlim(0, 1);
window.Show();
var result = new BrowserResult()
{
ResultType = BrowserResultType.UserCancel
};
webBrowser.Navigating += (s, e) =>
{
if (BrowserIsNavigatingToRedirectUri(e.Uri))
{
e.Cancel = true;
result = new BrowserResult()
{
ResultType = BrowserResultType.Success,
Response = e.Uri.AbsoluteUri
};
signal.Release();
window.Close();
}
};
window.Closing += (s, e) =>
{
signal.Release();
};
window.Content = webBrowser;
window.Show();
webBrowser.Source = new Uri(_options.StartUrl);
await signal.WaitAsync();
return result;
}
private bool BrowserIsNavigatingToRedirectUri(Uri uri)
{
return uri.AbsoluteUri.StartsWith(_options.EndUrl);
}

Set maxCout to 2 in SemaphoreSlim, it's wroks for me.
var signal = new SemaphoreSlim(0, 2);

Related

Use WebBrowser control in page instead of popup for Identity Server 4 login

I am using Identity Server 4 for authentication on my WPF MVVM app. Currently, when the user wants to log in, a popup window appears with the login screen and the info gets passed back through the OidcClientOptions. However, instead of a popup window, I want the login page to show up on a WebBrowser control in the actual application. How can I accomplish this?
My Login code:
public class Login
{
private OidcClient _oidcClient = null;
LoginResult result = null;
AccessToken accessToken = new AccessToken();
public async void LoginPage()
{
var options = new OidcClientOptions()
{
Authority = "https://localhost:5001/",
ClientId = "wpf",
ClientSecret = "secret",
Scope = "openid WebAPI",
RedirectUri = "http://localhost/signin-oidc",
Flow = OidcClientOptions.AuthenticationFlow.AuthorizationCode,
Browser = new WpfEmbeddedBrowser();
};
_oidcClient = new OidcClient(options);
try
{
result = await _oidcClient.LoginAsync();
}
catch (Exception ex)
{
return;
}
if (result.IsError)
{
}
else
{
accessToken.WriteToken(result.AccessToken);
App.Current.Properties["AccessToken"] = result.AccessToken;
}
}
}
Current WpfEmbeddedBrowser I'm using:
public class WpfEmbeddedBrowser : IBrowser
{
private BrowserOptions _options = null;
public WpfEmbeddedBrowser()
{
}
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
_options = options;
var window = new Window()
{
Width = 900,
Height = 625,
Title = "IdentityServer Login"
};
// Note: Unfortunately, WebBrowser is very limited and does not give sufficient information for
// robust error handling. The alternative is to use a system browser or third party embedded
// library (which tend to balloon the size of your application and are complicated).
var webBrowser = new WebBrowser();
var signal = new SemaphoreSlim(0, 1);
var result = new BrowserResult()
{
ResultType = BrowserResultType.UserCancel
};
webBrowser.Navigating += (s, e) =>
{
if (BrowserIsNavigatingToRedirectUri(e.Uri))
{
e.Cancel = true;
result = new BrowserResult()
{
ResultType = BrowserResultType.Success,
Response = e.Uri.AbsoluteUri
};
signal.Release();
window.Close();
}
};
window.Closing += (s, e) =>
{
signal.Release();
};
window.Content = webBrowser;
window.Show();
webBrowser.Source = new Uri(_options.StartUrl);
await signal.WaitAsync();
return result;
}
private bool BrowserIsNavigatingToRedirectUri(Uri uri)
{
return uri.AbsoluteUri.StartsWith(_options.EndUrl);
}
}

migrating from Azure Active Directory Graph Api to Microsoft Graph Api

I have an application I am signing in to using SSO office 365 to authenticate the user. I am also calling the azure active directory graph api to pull a list of all users in the organization. I want to stop using the azure active directory graph api (since it is being deprecated as of 2/2019) and move over to microsoft-graph api. If I use microsoft graph to pull users, will I also have to authenticate with diff way (not Azure)?
this is my current auth code in startup file:
public void ConfigureAuth(IAppBuilder app)
{
string strIssuers = ConfigurationManager.AppSettings["validIssuers"];
string[] validIssuers = strIssuers.Split(',');
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
ValidIssuers = validIssuers
}
});
}
in graph call:
public async Task<List<User>> GetAdUsers(string tid, string path = "users")
{
var identity = HttpContext.Current.User.Identity as ClaimsIdentity;
string email = identity?.Name;
var selectvalues = "";//(path.ToLower() == "users" ? "$select=*" : "");
List<User> tmpUsers;
string skipToken;
string skipTokenResult;
int skipTokenIndex;
string strAuth = "https://login.microsoftonline.com/" + tid + "/oauth2/v2.0/token";
var client = ConfigurationManager.AppSettings["ida:Audience"];
var secret = ConfigurationManager.AppSettings["clientSecret"];
string clientId = client;
string clientSecret = secret;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result = null;
AuthenticationContext _authContext = new AuthenticationContext(strAuth);
Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential creds
= new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecret);
result = await _authContext.AcquireTokenAsync("https://graph.microsoft.com", creds);
var _httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage Res = await _httpClient.GetAsync("https://graph.microsoft.com/v1.0/" + path + "?$top=999" + selectvalues);
if (Res.IsSuccessStatusCode)
{
string strJson = Res.Content.ReadAsStringAsync().Result;
JavaScriptSerializer json = new JavaScriptSerializer();
RootObject rootObj = json.Deserialize<RootObject>(strJson);
List<User> adUsers = rootObj.Value;
var parseRes = JObject.Parse(strJson);
bool stop = false;
while (!stop)
{
try
{
skipTokenResult = parseRes["#odata.nextLink"].Value<string>();
skipTokenIndex = skipTokenResult.IndexOf("skiptoken=");
skipToken = skipTokenResult.Substring(skipTokenIndex + 10, skipTokenResult.Length - skipTokenIndex - 10);
Res = await _httpClient.GetAsync("https://graph.microsoft.com/v1.0/" + path + "?$top=999&$skiptoken=" + skipToken + selectvalues);
if (Res.IsSuccessStatusCode)
{
strJson = Res.Content.ReadAsStringAsync().Result;
rootObj = json.Deserialize<RootObject>(strJson);
tmpUsers = rootObj.Value;
adUsers.AddRange(tmpUsers);
parseRes = JObject.Parse(strJson);
}
else
{
stop = true;
}
}
catch (ArgumentNullException) // no skip token, stop looping !!!!
{
stop = true;
}
}
return adUsers;
}
else
{
// return null;
throw new Exception("GetAdUsers: Graph API failed for path: " + path + ", tid: " + tid + ". Reason: " + Res.ReasonPhrase);
}
}
//UPDATE: I was able to update the code to use SOAP Microsoft Graph API like this:
public GraphServiceClient AuthGraph(string tid, string groupId)
{
try
{
var clientId = ConfigurationManager.AppSettings["ida:Audience"];
var clientSecret = ConfigurationManager.AppSettings["ida:clientSecret"];
var tenantID = tid;
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
//.WithRedirectUri(redirectUri)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
return graphClient;
}
catch (Exception e)
{
throw e;
}
}
public async Task<List<User>> GetAdUsers(string tid, string groupId)
{
try
{
GraphServiceClient graphClient = AuthGraph(tid, groupId);
var graphUsers = await graphClient.Users
.Request()
.GetAsync();
List<User> users = graphUsers.Select(x => new User
{
Id = x.Id,
BusinessPhones = x.BusinessPhones.ToArray(),
DisplayName = x.DisplayName,
GivenName = x.GivenName,
JobTitle = x.JobTitle,
Mail = x.Mail,
MobilePhone = x.MobilePhone,
OfficeLocation = x.OfficeLocation,
PreferredLanguage = x.PreferredLanguage,
Surname = x.Surname,
UserPrincipalName = x.UserPrincipalName
}
).ToList();
if (!string.IsNullOrEmpty(groupId))
{
var membersInGroups = await GetNonSSOUsers(Globals.mghsTid, groupId);
users.AddRange(membersInGroups);
}
return users;
}
catch(Exception ex)
{
_errService.LogError("UserController.Update", tid, ex.HResult, ex.ToString().Substring(0, Math.Min(ex.ToString().Length, Globals.maxErrDescLen)), "getAdUsersService", 1, DateTime.Now.ToString());
throw ex;
}
}
public async Task<List<User>> GetNonSSOUsers(string tid, string groupId)
{
try
{
GraphServiceClient graphClient = AuthGraph(tid, groupId);
var members = await graphClient.Groups[groupId].Members
.Request()
.GetAsync();
List<User> users = new List<User>();
//while (members.NextPageRequest != null && (members = await members.NextPageRequest.GetAsync()).Count > 0)
//{
foreach (var member in members)
{
if (member is Microsoft.Graph.User)
{
var user = (Microsoft.Graph.User)member;
users.Add(new User
{
Id = user.Id,
BusinessPhones = user.BusinessPhones.ToArray(),
DisplayName = user.DisplayName,
GivenName = user.GivenName,
JobTitle = user.JobTitle,
Mail = user.Mail,
MobilePhone = user.MobilePhone,
OfficeLocation = user.OfficeLocation,
PreferredLanguage = user.PreferredLanguage,
Surname = user.Surname,
UserPrincipalName = user.UserPrincipalName
});
}
}
// }
return users;
}
catch (Exception e)
{
throw e;
}
}
The Microsoft Graph API is also protected under Azure AD. So, basically, you just need to add and grant necessary Graph API permissions to your application registered in Azure AD.
After that, you can call the Microsoft Graph API by adding an authorization header.

No 'Access-Control-Allow-Origin' header is present on the requested resource error when [Authorize] is placed on any method in a controller

I have been following the tutorials here to build my application. Everything works well at the authentication side. Now, the problem I am having is when I move to the resource server to retrieve data. If I place the [Authorize] on any method in the resource server I get the error message "No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin is therefore not allowed access. The response had HTTP status code 500.". If I remove it everything works fine but I am unable access any claims or roles associated with the user
Excerpts of the startup.cs code of my AuthServer is as follows
public class Startup
{
string PublicHostUri { get { return "https://localhost:44354"; } }
private readonly IHostingEnvironment _environment;
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
_environment = env;
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration["Data:UserAccConnection:ConnectionString"];
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "reportbook.auth.pfx"), "");
var reportbookConnnectionString = Configuration["Data:ReportBookDbConnection:connectionString"];
services.AddDbContext<ReportBookDbContext>(options =>
options.UseSqlServer(reportbookConnnectionString));
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddDbContext<UserDbContext>(options =>
options.UseSqlServer(connectionString, b => b.MigrationsAssembly(migrationsAssembly)));
// Register the Identity services.
services.AddIdentity<ApplicationUser, UserRole>()
.AddEntityFrameworkStores<UserDbContext, Guid>()
.AddDefaultTokenProviders();
services.AddCors();
services.AddMvc();
services.AddIdentityServer()
.AddDefaultEndpoints()
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString,
options => options.MigrationsAssembly(migrationsAssembly)))
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString,
options => options.MigrationsAssembly(migrationsAssembly)))
.SetSigningCredential(cert)
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<IdentityWithAdditionalClaimsProfileService>();
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
services.AddTransient<IUnitOfWorkAsync, UnitOfWork>();
services.AddScoped<IDataContextAsync, ReportBookDbContext>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
InitializeDbTestData(app);
}else
{
app.UseExceptionHandler(
builder =>
{
builder.Run(
async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
}
});
});
}
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseCors(builder =>
builder.AllowAnyOrigin()
.AllowCredentials()
.AllowAnyHeader()
.AllowAnyMethod());
app.UseCsp(options => options.DefaultSources(directive => directive.Self())
.ImageSources(directive => directive.Self()
.CustomSources("*"))
.ScriptSources(directive => directive.Self()
.UnsafeInline())
.StyleSources(directive => directive.Self()
.UnsafeInline()));
app.UseXContentTypeOptions();
app.UseXfo(options => options.Deny());
app.UseXXssProtection(options => options.EnabledWithBlockMode());
app.UseIdentity();
app.UseIdentityServer();
//app.UseMvc(routes =>
//{
// routes.MapRoute(
// name: "default",
// template: "{controller=Home}/{action=Index}/{id?}");
//});
app.UseMvcWithDefaultRoute();
app.UseMvc();
//databaseInitializer.Seed(app).GetAwaiter().GetResult();
}
private static void InitializeDbTestData(IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>().Database.Migrate();
scope.ServiceProvider.GetRequiredService<UserDbContext>().Database.Migrate();
var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
if (!context.Clients.Any())
{
foreach (var client in Clients.Get())
{
context.Clients.Add(client.ToEntity());
}
context.SaveChanges();
}
if (!context.Scopes.Any())
{
foreach (var clientSope in Scopes.Get())
{
context.Scopes.Add(clientSope.ToEntity());
}
context.SaveChanges();
}
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<UserRole>>();
if (!userManager.Users.Any())
{
foreach (var newUser in Users.Get())
{
ApplicationUser user = new ApplicationUser();
user.Id = new Guid();
user.EmailConfirmed = true;
user.UserName = newUser.Email;
user.UserNo = newUser.UserNo;
user.FirstName = newUser.FirstName;
user.LastName = newUser.LastName;
user.Gender = newUser.Gender;
user.UserCategory = newUser.UserCategory;
user.ZoneInfo = newUser.ZoneInfo;
userManager.CreateAsync(user, "Password123!").Wait();
userManager.AddClaimAsync(user, new Claim("UserCategory", user.UserCategory)).Wait();
foreach (var role in newUser.UserRoles)
{
if (!roleManager.RoleExistsAsync(role).GetAwaiter().GetResult())
{
UserRole userRole = new UserRole();
userRole.Id = new Guid();
userRole.Name = role;
roleManager.CreateAsync(userRole).Wait();
}
userManager.AddToRoleAsync(user, role).Wait();
userManager.AddClaimAsync(user, new Claim(JwtClaimTypes.Role, role)).Wait();
}
}
}
}
}
}
Excerpts of startup.cs file of the resource server is as follows
public class Startup
{
private IHostingEnvironment _env { get; set; }
public Startup(IHostingEnvironment env)
{
_env = env;
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
//private static void InitializeDbTestData(IApplicationBuilder app)
//{
// using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
// {
// scope.ServiceProvider.GetRequiredService<ReportBookDbContext>().Database.Migrate();
// }
//}
// This method gets called by the runtime. Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
var folderForKeyStore = Configuration["Data:keystore:KeyStoreFolderWhichIsBacked"];
var cert = new X509Certificate2(Path.Combine(_env.ContentRootPath, "reportbook.auth.pfx"), "");
services.AddDataProtection()
.SetApplicationName("ReportBook")
.ProtectKeysWithDpapiNG("CERTIFICATE=Hashid:" + cert.Thumbprint,flags: DpapiNGProtectionDescriptorFlags.None);
services.AddDbContext<ReportBookDbContext>(options =>
options.UseSqlServer(Configuration["Data:ReportBookDbConnection:connectionString"],
b => b.MigrationsAssembly("ReportBook.Resource")));
// Add framework services.
services.AddCors();
services.AddApplicationInsightsTelemetry(Configuration);
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseDeveloperExceptionPage();
app.UseStatusCodePagesWithReExecute("/error");
if (env.IsDevelopment())
{
//InitializeDbTestData(app);
}
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
IdentityServerAuthenticationOptions identityServerValidationOptions = new IdentityServerAuthenticationOptions
{
Authority = "https://localhost:44354/",
ScopeName = "resource_server",
ScopeSecret = new IdentityServer4.Models.Secret("scopeSecret".Sha256()).ToString(),
AutomaticAuthenticate = true,
SupportedTokens = SupportedTokens.Both,
AutomaticChallenge = true
};
app.UseIdentityServerAuthentication(identityServerValidationOptions);
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseExceptionHandler(
builder =>
{
builder.Run(
async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
}
});
});
app.UseCors(builder =>
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials());
app.UseMvc();
}
}
Below is the an excerpt of the the controller whose method I am trying to reach
[HttpGet("GetInstitutions")]
//[Authorize]
public IActionResult GetInstitutions([FromQuery]InstitutionSearchQry model)
{
var authorisation = Request.Headers["Authorization"];
bool auth = User.Identity.IsAuthenticated;
IEnumerable<Institution> _institutions = null;
string userCategory = User.Claims.Where(a => a.Type == "UserCategory").Select(a => a.Value).FirstOrDefault().ToString();
string zoneInfo = User.Claims.Where(a => a.Type == "ZoneInfo").Select(a => a.Value).FirstOrDefault().ToString();
string userNo = User.Claims.Where(a => a.Type == "UserNo").Select(a => a.Value).FirstOrDefault().ToString();
bool admin = User.IsInRole("Admin");
List<Student> students = new List<Student>();
//Institution institution = _institutionService.Find(a => a.InstitutionID == zoneInfo);
var pagination = Request.Headers["Pagination"];
if (!string.IsNullOrEmpty(pagination))
{
string[] vals = pagination.ToString().Split(',');
int.TryParse(vals[0], out page);
int.TryParse(vals[1], out pageSize);
}
switch (userCategory)
{
case "Guardian":
{
students = _guardianService.GetStudents(userNo).ToList();
_institutions = _admissionService.GetInstitutions(students.Select(a => a.StudentID).ToList(),model.StartYear,model.EndYear, s => s.Term.AcademicYear.Institution.UniversityInstitutes.Select(a => a.University));
}
break;
case "Student":
{
_institutions = _admissionService.GetInstitution(userNo,s=>s.Term.AcademicYear.Institution.UniversityInstitutes.Select(a=>a.University));
}
break;
default:
{
_institutions = _institutionService.GetInstitutions(a => a.AdministrativeStructure.ZoneInfo == zoneInfo && a.Level.LevelName==model.Level, page, pageSize, out totalCount, s => s.AdministrativeStructure, s => s.Level,s=>s.UniversityInstitutes.Select(a=>a.University));
}
break;
}
if (!String.IsNullOrEmpty(model.Level) && model.Level != "myschool")
{
_institutions = _institutions.Where(a => a.Level.LevelName == model.Level);
}
var totalPages = (int)Math.Ceiling((double)totalCount / pageSize);
Response.AddPagination(page, pageSize, totalCount,totalPages);
Response.AddIdentityInfo(userCategory, admin, userNo, zoneInfo);
IEnumerable<InstitutionDataViewModel> _instDataModel = Mapper.Map<IEnumerable<Institution>, IEnumerable<InstitutionDataViewModel>>(_institutions);
return new OkObjectResult(_instDataModel);
}
The following is the angular 2 code from where the call is made to the resource server
#Injectable()
export class InstitutionService {
private resourceApiUrl: string;
private headers: Headers;
private storage: any;
private actionUrl: string;
public totalItems: number;
constructor(private _http: Http,
private itemsService: ItemsService,
private _configuration: Configuration,
private _router: Router,
private _authService: AuthService) {
this.resourceApiUrl = `${_configuration.resourceServer}api/Institution/`;
}
private SetHeaders(page?: number, itemsPerPage?: number) {
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept', 'application/json');
if (page != null && itemsPerPage != null) {
this.headers.append('Pagination', page + ',' + itemsPerPage);
}
var token = this._authService.GetToken();
if (token !== "") {
let tokenValue = 'Bearer ' + token;
console.log("tokenValue:" + tokenValue);
this.headers.append('Authorization', tokenValue);
}
}
public GetInstitutions = (InstitutionSearchQry?: any, page?: number, itemsPerPage?: number): Observable<PaginatedResult<IInstitution[]>> => {
this.SetHeaders(page, itemsPerPage);
var paginatedResult: PaginatedResult<IInstitution[]> = new PaginatedResult<IInstitution[]>();
let options = new RequestOptions({ headers: this.headers, body: '' });
if (!InstitutionSearchQry.level) {
this.actionUrl = "GetInstitutions";
} else {
this.actionUrl = "GetInstitutions/", InstitutionSearchQry;
}
return this._http.get(this.resourceApiUrl + this.actionUrl, options)
.map((res: Response) => {
//console.log(res.headers.keys());
paginatedResult.result = res.json();
if (res.headers.get("Pagination") != null) {
//var pagination = JSON.parse(res.headers.get("Pagination"));
var paginationHeader: Pagination = this.itemsService.getSerialized<Pagination>(JSON.parse(res.headers.get("Pagination")));
paginatedResult.pagination = paginationHeader;
}
if (res.headers.get("IdentityInfo") != null) {
var identityInfo: IdentityInfo = this.itemsService.getSerialized<IdentityInfo>(JSON.parse(res.headers.get("IdentityInfo")));
paginatedResult.identityInfo = identityInfo;
}
this.totalItems = paginatedResult.pagination.TotalItems;
return paginatedResult;
}).catch(this.handleError);
};
}
So basically the authorisation information provided at the AuthServer side is not reaching the resource server.
As one can see I have added the 'CORS' service in both files.
Use this plugin for chrome browser. get from here

How do I update the UI from a HttpWebRequest?

In my Mainpage.xaml.cs file I have a function that creates an instance of another class and tries to download a webpage using a HttpWebRequest from that instance. The problem is, once I've managed to download the webpage I can't send it back to the main UI thread. I've tried using Deployment.Current.Dispatcher.BeginInvoke to send the webpage back to a TextBlock I have waiting, but when I try I get an error telling me that I can't access the TextBlock from the other class. Is there any way to pass data between two threads without using LocalStorage?
EDIT: code below:
MainPage:
private void button1_Click(object sender, RoutedEventArgs e)
{
Member m = new Member(name, id);
}
Member class:
public Member(String Member, String API)
{
APIKey = API;
MemberName = Member;
this.super = super;
DoSend(method, string, "", null);
}
public void DoSend(string method, string url, string body, string mimetype)
{
if (WebRequest.RegisterPrefix("https://",System.Net.Browser.WebRequestCreator.ClientHttp)) {
HttpWebRequest request = WebRequest.Create(makeURI(url)) as HttpWebRequest;
request.Method = method;
request.Headers["X-NFSN-Authentication"] = MakeAuthHeader(url,body);
if (body != "")
{
byte[] bodyData = Encoding.UTF8.GetBytes(body);
request.ContentType = mimetype;
//Stuff Should Happen Here
}
else
doStuff(request);
}
public void doStuff(HttpWebRequest httpReq)
{
httpReq.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
ResponseBlock.Text = response; //Invalid cross-thread reference
}
}, httpReq);
}
MainPage:
customClass.DownloadPage((result) =>
{
textBlock.Text = result;
},
(exception) =>
{
MessageBox.Show(exception.Message);
});
CustomClass:
public void DownloadPage(Action<string> callback, Action<Exception> exception)
{
WebClient webClient = new WebClient();
webClient.DonwloadStringCompleted += (s, e) =>
{
if (e.Error == null)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
callback(e.Result);
});
}
else
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
exception(e.Error);
});
}
};
webClient.DonwloadStringAsync();
}

Google OAuth2.0 - Windows phone 7

Experts,
I'm new to OAuth,
I'm writing an small App in windows Phone 7, I'm using OAuth2 for google contacts, I need to get the initial URL API (1) to get the Token
1) https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.google.com/m8/feeds/&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&hl=en-US&from_login=1&as=NEW_AS&pli=1
I got the success code, and when I'm trying to use this
https://www.google.com/m8/feeds/contacts/default/full?access_token=TOKEN_CODE
but I'm getting 401 error back,
Can you please advice, what mistake i'm going.
I've taken Twitter OAuth example as base, and doing modifications.
CODE
var uri = new Uri(url);
var request = BuildOAuthWebRequest(url, null);
MakeGetRequest(callback, request);
private static HttpWebRequest BuildOAuthWebRequest( string url, string realm)
{
var header = new StringBuilder();
var request = (HttpWebRequest)WebRequest.Create(url);
return request;
}
private static void MakeGetRequest(EventHandler<OAuthEventArgs> callback, HttpWebRequest request)
{
var asyncState = request.BeginGetResponse(new AsyncCallback((asyncRes) =>
{
HttpWebResponse response = null;
try
{
//request has returned
response = (HttpWebResponse)request.EndGetResponse(asyncRes);
if (response.StatusCode == HttpStatusCode.OK)
{
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
var token = sr.ReadToEnd();
callback(null, new OAuthEventArgs() { Response = token });
}
}
}
catch (WebException we)
{
string t = new StreamReader(we.Response.GetResponseStream()).ReadToEnd();
callback(null, new OAuthEventArgs() { Error = we, ErrorMessage = t, IsError = true });
}
catch (Exception e)
{
callback(null, new OAuthEventArgs() { Error = e, ErrorMessage = e.Message, IsError = true });
}
finally
{
if (response != null)
response.Close();
}
}), null);
}

Resources