LinqToTwitter Search Never Returns - linq-to-twitter

I am attempting to use LinqToTwitter to search twitter. It works fine as run in an NUnit test but it does not work with ASP.NET or as a WinForm app. I am not sure what Authorizer to use.
public async Task<Search> SearchTwitter(string searchWords)
{
var twitterCtx = BuildTwitterContext();
Task<Search> searchResponse = (from search in twitterCtx.Search
where search.Type == SearchType.Search &&
search.Query == searchWords
select search)
.SingleOrDefaultAsync();
return await searchResponse;
}
private static TwitterContext BuildTwitterContext()
{
IAuthorizer authorizer;
if (HttpContext.Current == null)
authorizer = new PinAuthorizer();
else
authorizer = new AspNetSignInAuthorizer();
InMemoryCredentialStore credentialStore = new InMemoryCredentialStore();
credentialStore.ConsumerKey = consumerKey;
credentialStore.ConsumerSecret = consumerSecret;
credentialStore.OAuthToken = accessToken;
credentialStore.OAuthTokenSecret = accessTokenSecret;
authorizer.CredentialStore = credentialStore;
var twitterCtx = new TwitterContext(authorizer);
return twitterCtx;
}

ASP.NET is different because of the page redirections where you start the authorization and then finish after Twitter redirects back. Here's the LINQ to Twitter documentation that will explain how OAuth works and give you a better idea on which authorizers to use:
https://github.com/JoeMayo/LinqToTwitter/wiki/Learning-to-use-OAuth
The L2T source code also has demos. Here's an OAuth controller demo:
https://github.com/JoeMayo/LinqToTwitter/blob/master/New/Linq2TwitterDemos_Mvc/Controllers/OAuthController.cs
public class OAuthController : AsyncController
{
public ActionResult Index()
{
return View();
}
public async Task<ActionResult> BeginAsync()
{
//var auth = new MvcSignInAuthorizer
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
}
};
string twitterCallbackUrl = Request.Url.ToString().Replace("Begin", "Complete");
return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
}
public async Task<ActionResult> CompleteAsync()
{
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore()
};
await auth.CompleteAuthorizeAsync(Request.Url);
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database,
// isolated storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent
// queries to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let
// you make queries without re-authorizing.
//
//var credentials = auth.CredentialStore;
//string oauthToken = credentials.OAuthToken;
//string oauthTokenSecret = credentials.OAuthTokenSecret;
//string screenName = credentials.ScreenName;
//ulong userID = credentials.UserID;
//
return RedirectToAction("Index", "Home");
}
}
Notice that it uses a WebAuthorizer/SessionStateCredentials pair and separates the start of authorization with a separate action method (specified via callback) for completion.
The following demo shows how to perform OAuth in a WinForms app:
https://github.com/JoeMayo/LinqToTwitter/blob/master/New/Demos/Linq2TwitterDemos_WindowsForms/OAuthForm.cs
public partial class OAuthForm : Form
{
PinAuthorizer pinAuth = new PinAuthorizer();
public OAuthForm()
{
InitializeComponent();
}
async void OAuthForm_Load(object sender, EventArgs e)
{
pinAuth = new PinAuthorizer
{
// Get the ConsumerKey and ConsumerSecret for your app and load them here.
CredentialStore = new InMemoryCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
},
// Note: GetPin isn't used here because we've broken the authorization
// process into two parts: begin and complete
GoToTwitterAuthorization = pageLink =>
OAuthWebBrowser.Navigate(new Uri(pageLink, UriKind.Absolute))
};
await pinAuth.BeginAuthorizeAsync();
}
async void SubmitPinButton_Click(object sender, EventArgs e)
{
await pinAuth.CompleteAuthorizeAsync(PinTextBox.Text);
SharedState.Authorizer = pinAuth;
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database, isolated storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent queries to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let you make queries without re-authorizing.
//
//var credentials = pinAuth.CredentialStore;
//string oauthToken = credentials.OAuthToken;
//string oauthTokenSecret = credentials.OAuthTokenSecret;
//string screenName = credentials.ScreenName;
//ulong userID = credentials.UserID;
//
Close();
}
}
In this case, you can use a PinAuthorizer with an InMemoryCredentialStore. If you look at that demo, it uses a Web Browser control to navigate to Twitter and manage the OAuth flow.
Look at the URL above for the Learning to use OAuth for examples of other IAuthorizer derived types that you can use in different scenarios. Also, download the source code and step through with the debugger to get a feel for the OAuth workflow.

Related

Get Azure B2C User Directory listing in Azure Function with MSAL.NET

We are currently getting a list of our Users using MS Graph and the directoryObjects/getByIds endpoint.
In the Startup of the ASP NET Core API we are using Microsoft.IdentityModel.Clients.ActiveDirectory and this code
services.AddHttpClient("GraphApi", async hc =>
{
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/" + this.configuration["GraphApi:Tenant"]);
ClientCredential credential = new ClientCredential(this.configuration["GraphApi:ClientId"], this.configuration["GraphApi:ClientSecret"]);
hc.BaseAddress = new Uri($"https://graph.microsoft.com/v1.0/");
hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
AuthenticationResult result = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", credential);
hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
});
I am creating a new Azure Function and need to do the same thing again. I was going to use the same code and Microsoft.IdentityModel.Clients.ActiveDirectory but that package has been deprecated and we should be using Microsoft.Identity.Client.
I can see lots of samples for various scenarios but they seem to be all calling the public MS Graph whereas I want to get the users from our own Azure B2C. Can someone point me at the right resources\demo.
The Azure Function will not be running in the context of a user so Managed Identity or Client Secret approach would be useful
I have implemented a similar kind of scenario for getting Azure AD user but different way in MVC
CODE
I have used these NuGet packages
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
Startup class
public class Startup
{
string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false // This is a simplification
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
},
}
);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
HomeController
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge( new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
public void SignOut()
{
HttpContext.GetOwinContext().Authentication.SignOut( OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
ClaimsController
public ActionResult Index()
{
var userClaims = User.Identity as System.Security.Claims.ClaimsIdentity;
ViewBag.Name = userClaims?.FindFirst("name")?.Value;
ViewBag.Username = userClaims?.FindFirst("preferred_username")?.Value;
ViewBag.Subject = userClaims?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
ViewBag.TenantId = userClaims?.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value;
return View();
}
I tried to cover all possible implementations. Hope it will work in your case
Thanks

Implement Salesforce Authentication (OAuth) in Xamarin Forms App

This article explains if anyone wants to integrate mobile app with Salesforce App and require to authenticate the app with salesforce OAuth
iOS Implementation-
Step 1: To implement Deep Link define URL Schemes in info.plist according to connected app : The callback URL
Defining URL Schemes
Step 2- Implement 'OpenUrl' method in AppDelegate to receive the deep link call in your app
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
var deepLinkURL = url;
url = null;
App myApp = App.Current as myApp.App;
if (myApp != null && deepLinkURL != null)
{
LandingPage.DeepLinkURL = deepLinkURL;
}
return true;
}
Step 3- Now you have to check whether user is coming first time or already authenticated:
public async static Task HandleDeepLink()
{
//await SecureStorage.SetAsync(AppConstant.userId, "");
/* Deep Link
* Check if user is same
* user is already loggedIn
* Token expired
*/
try
{
string data = DeepLinkURL.ToString();
if (data.Contains("userId"))
{
// extract and save all the parameters
userId = HttpUtility.ParseQueryString(DeepLinkURL.Query).Get("userId");
// if user not authenticated OR if different user
isSameAsLastUser = await oAuth.ValidateUser(userId);
if (isSameAsLastUser)
{
// is already logged in
var isUserLoggedIn = await oAuth.IsUserLoggedIn();
if (isUserLoggedIn)
{
// navigate to scan
await SecureStorage.SetAsync(AppConstant.uniqueId, uniqueId);
Application.Current.MainPage = new NavigationPage(new MainPage());
}
else
{
Application.Current.MainPage = new NavigationPage(new Authentication());
}
}
else
{
// clear previous values in secure storage
AppConfig.SaveSFParameters("false");
Application.Current.MainPage = new NavigationPage(new Authentication());
}
}
// Handle oAuth
//Extract the Code
else if (data.Contains("code"))
{
var code = HttpUtility.ParseQueryString(DeepLinkURL.Query).Get("code");
/*Exchange the code for an access token
* Save token
* LoggedInTrue
*/
if (CrossConnectivity.Current.IsConnected)
{
var authInfo = await oAuth.GetAccessToken(code);
Console.WriteLine("auth token - " + authInfo.AccessToken);
oAuth.SaveAuthInfo(authInfo);
// save user info | set user logged in to true
AppConfig.SaveSFParameters(userId,"true");
// retrieve all the parameters and pass here
Application.Current.MainPage = new NavigationPage(new MainPage(userId));
}
else
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.DisplayAlert(AppConstant.Alert, Resource.Resources.AlertMissingConnectivity, AppConstant.Ok);
});
}
}
else if (data.Contains("error"))
{
var error = HttpUtility.ParseQueryString(DeepLinkURL.Query).Get("error");
//TODO: where the user should be redirected in case of OAuth error
}
}
catch(Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
Application.Current.MainPage.DisplayAlert("Alert", "There is some error while redirecting user to scan app, Please contact administrator", "Ok");
});
}
}
}
public partial class Authentication : ContentPage
{
protected override void OnAppearing()
{
base.OnAppearing();
if (CrossConnectivity.Current.IsConnected)
{
var authUrl = Common.FormatAuthUrl(AppConfiguration.authEndPointURL,
ResponseTypes.Code, AppConfiguration.clientId,
HttpUtility.UrlEncode(AppConfiguration.redirectUri));
Browser.OpenAsync(new Uri(authUrl), BrowserLaunchMode.SystemPreferred);
}
}
}
Step 4 - Once user is authenticated successfully salesforce returns the authorization code
Step 5- Now you have to make salesforce API call to retrieve the access token by supplying the authorization code
Step 6- Once you receive the access toekn then save access token, refresh token in your app using 'Xamarin SecureStorage'
async public static void SaveAuthInfo(AuthToken authInfo)
{
try
{
await SecureStorage.SetAsync("oauth_token", authInfo.AccessToken);
await SecureStorage.SetAsync("oauth_Id", authInfo.Id);
await SecureStorage.SetAsync("oauth_InstanceUrl", authInfo.InstanceUrl);
await SecureStorage.SetAsync("oauth_RefreshToken", authInfo.RefreshToken);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
// Possible that device doesn't support secure storage on device.
}
}
Step 7 - Now redirect user to landing page

How to store additional data per user like session on Owin using Bearer Token

I need to store a token for thirdy party software calls on my controller after my client sign in, so I tried to save this on User Claims:
public class BaseController : ApiController
{
private const string Token = "thirdyparty.token";
private string Token
{
set
{
// Here I want to store a token in any way (Session, Cache, etc)
var claimsIdentity = (ClaimsIdentity)User.Identity;
var claims = claimsIdentity.Claims;
var tokenClaim = claims.FirstOrDefault(x => x.Type == Token);
if (Token != null)
{
claimsIdentity.RemoveClaim(tokenClaim);
}
claimsIdentity.AddClaim(new Claim(Token, value));
}
get
{
// Here I want to get the token
var claimsIdentity = (ClaimsIdentity)User.Identity;
var claims = claimsIdentity.Claims;
var tokenClaim = claims.FirstOrDefault(x => x.Type == Token);
return tokenClaim?.Value;
}
}
}
This did not work, my new Claim disappeared every time a new request is made.
So, how can I store some additional information per user?
The problem is that the claims are part of the bearer token.
So even if you add the claim to the current identity the next request will
have the old claim values as they are part of the token sent with the new request.
So, if you add a claim you need to generate a new token as well and return that to the client.
One way to generate a new token is to store the OAuthAuthorizationServerOptions, used in the
Startup.cs class, as a static variable and then use that where it's needed
public class Startup
{
public static OAuthAuthorizationServerOptions OAuthServerOptions { get; private set; }
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
//....add the rest
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new AuthProvider() //Your derived OAuthAuthorizationServerProvider
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
Then to generate a new token
var claimsIdentity = ... //The claim identity after you have added the new claims
var newToken = Startup.OAuthServerOptions.AccessTokenFormat.Protect(new AuthenticationTicket(claimsIdentity, new AuthenticationProperties()));

Why Context.User.Identity.Name is empty from Xamarin?

I can't figure out why when I try to connect from Xamarin Context.User.Indetity.Name is empty. Is there anything special I need to do? I logged in to the server and the user has a connection stablished. After that I use the following code:
var Connection = new HubConnection(Url);
_hub = Connection.CreateHubProxy(hubName);
_hub.On(srvEvent, onData);
await Connection.Start();
But I never get the username. What am I doing wrong?
Here's the code for the server:
var name = Context.User.Identity.Name;
Connections.Add(name, Context.ConnectionId);
return base.OnConnected();
It works when it comes from the web app, not from the xamarin app.
Thanks!
Here is the code I was telling you about.
I'm using an external OAuth2 server for authentication, so I must pass the access token to SignalR somehow, because SignalR uses web sockets for the messages back and forth I can't pass the access token in the header because this is not supported by web sockets.
I'm passing that access token as a query string parameter this way (Javascript client)
$.connection.hub.qs = "access_token=" + mytoken;
Then on my SignalR I added a middleware that takes that query string and adds it to the header as an Authorization header using Bearer Token. This is done this way in my startup class
app.UseAuthQSTokenExtractor();
The code for the middleware is this one
namespace Owin
{
public static class AuthorizationQSTokenExtractorExtension
{
public static void UseAuthQSTokenExtractor(this IAppBuilder app)
{
app.Use<AuthorizationQsTokenExtractorMiddleware>();
}
}
}
namespace Chat.Middleware
{
public class AuthorizationQsTokenExtractorMiddleware : OwinMiddleware
{
public AuthorizationQsTokenExtractorMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
Debug.WriteLine("signalr-auth-middleware");
string bearerToken = context.Request.Query.Get("access_token");
Debug.WriteLine("signar-bearer: " + bearerToken);
if (bearerToken != null)
{
TokenHelper.DecodeAndWrite(bearerToken);
string[] authorization = { "Bearer " + bearerToken };
context.Request.Headers.Add("Authorization", authorization);
}
await Next.Invoke(context);
}
}
My startup class then looks like this
app.UseCors(CorsOptions.AllowAll);
app.UseAuthQSTokenExtractor();
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
var hubConfiguration = new HubConfiguration ();
hubConfiguration.EnableDetailedErrors = true;
app.RunSignalR(hubConfiguration);
You can see in the code above where I tell SignalR to use the Oauth2 Server, that code is this one
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
After all this is set up I have access to my Context.User.Identity.Name and if you want to get the others IdentityClaim you can do this
var identity = Context.User.Identity as ClaimsIdentity;
Which I'm using that code above to get the subjectId (userid) like this
public static string[] GetIdentityClaimsIssSub(HubCallerContext Context)
{
var identity = Context.User.Identity as ClaimsIdentity;
if (identity == null)
return null;
var issuerFromIdentity = identity.FindFirst("iss");
var subFromIdentity = identity.FindFirst("sub");
if (issuerFromIdentity == null || subFromIdentity == null)
return null;
return new string[] { issuerFromIdentity.Value, subFromIdentity.Value };
}
I hope it helps

Azure Notification Hub and WP8 Intermitant notifications

This is a fairly long piece of code but I am getting nowhere with this and cannot see any issues, although I am new to using notification hubs. I am trying to register for targeted notifications (the logged on user) using the notification hub in Azure. After the registration, a test notification is sent.
The issue I am having is that sometimes the notification is sent to the device, and sometimes it is not. It mostly isn't but occasionally when I step through the code on the server, i will get the notification on the emulator come through. Once when I deployed the app to my phone the notification came though on the emulator! I cannot discover a pattern.
My Controller class looks like this;
private NotificationHelper hub;
public RegisterController()
{
hub = NotificationHelper.Instance;
}
public async Task<RegistrationDescription> Post([FromBody]JObject registrationCall)
{
var obj = await hub.Post(registrationCall);
return obj;
}
And the helper class (which is used elsewhere so is not directly in the controller) looks like this;
public static NotificationHelper Instance = new NotificationHelper();
public NotificationHubClient Hub { get; set; }
// Create the client in the constructor.
public NotificationHelper()
{
var cn = "<my-cn>";
Hub = NotificationHubClient.CreateClientFromConnectionString(cn, "<my-hub>");
}
public async Task<RegistrationDescription> Post([FromBody] JObject registrationCall)
{
// Get the registration info that we need from the request.
var platform = registrationCall["platform"].ToString();
var installationId = registrationCall["instId"].ToString();
var channelUri = registrationCall["channelUri"] != null
? registrationCall["channelUri"].ToString()
: null;
var deviceToken = registrationCall["deviceToken"] != null
? registrationCall["deviceToken"].ToString()
: null;
var userName = HttpContext.Current.User.Identity.Name;
// Get registrations for the current installation ID.
var regsForInstId = await Hub.GetRegistrationsByTagAsync(installationId, 100);
var updated = false;
var firstRegistration = true;
RegistrationDescription registration = null;
// Check for existing registrations.
foreach (var registrationDescription in regsForInstId)
{
if (firstRegistration)
{
// Update the tags.
registrationDescription.Tags = new HashSet<string>() {installationId, userName};
// We need to handle each platform separately.
switch (platform)
{
case "windows":
var winReg = registrationDescription as MpnsRegistrationDescription;
winReg.ChannelUri = new Uri(channelUri);
registration = await Hub.UpdateRegistrationAsync(winReg);
break;
case "ios":
var iosReg = registrationDescription as AppleRegistrationDescription;
iosReg.DeviceToken = deviceToken;
registration = await Hub.UpdateRegistrationAsync(iosReg);
break;
}
updated = true;
firstRegistration = false;
}
else
{
// We shouldn't have any extra registrations; delete if we do.
await Hub.DeleteRegistrationAsync(registrationDescription);
}
}
// Create a new registration.
if (!updated)
{
switch (platform)
{
case "windows":
registration = await Hub.CreateMpnsNativeRegistrationAsync(channelUri,
new string[] {installationId, userName});
break;
case "ios":
registration = await Hub.CreateAppleNativeRegistrationAsync(deviceToken,
new string[] {installationId, userName});
break;
}
}
// Send out a test notification.
await SendNotification(string.Format("Test notification for {0}", userName), userName);
return registration;
And finally, my SendNotification method is here;
internal async Task SendNotification(string notificationText, string tag)
{
try
{
var toast = PrepareToastPayload("<my-hub>", notificationText);
// Send a notification to the logged-in user on both platforms.
await NotificationHelper.Instance.Hub.SendMpnsNativeNotificationAsync(toast, tag);
//await hubClient.SendAppleNativeNotificationAsync(alert, tag);
}
catch (ArgumentException ex)
{
// This is expected when an APNS registration doesn't exist.
Console.WriteLine(ex.Message);
}
}
I suspect the issue is in my phone client code, which is here and SubscribeToService is called immediately after WebAPI login;
public void SubscribeToService()
{
_channel = HttpNotificationChannel.Find("mychannel");
if (_channel == null)
{
_channel = new HttpNotificationChannel("mychannel");
_channel.Open();
_channel.BindToShellToast();
}
_channel.ChannelUriUpdated += async (o, args) =>
{
var hub = new NotificationHub("<my-hub>", "<my-cn>");
await hub.RegisterNativeAsync(args.ChannelUri.ToString());
await RegisterForMessageNotificationsAsync();
};
}
public async Task RegisterForMessageNotificationsAsync()
{
using (var client = GetNewHttpClient(true))
{
// Get the info that we need to request registration.
var installationId = LocalStorageManager.GetInstallationId(); // a new Guid
var registration = new Dictionary<string, string>()
{
{"platform", "windows"},
{"instId", installationId},
{"channelUri", _channel.ChannelUri.ToString()}
};
var request = new HttpRequestMessage(HttpMethod.Post, new Uri(ApiUrl + "api/Register/RegisterForNotifications"));
request.Content = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json");
string message;
try
{
HttpResponseMessage response = await client.SendAsync(request);
message = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
message = ex.Message;
}
_registrationId = message;
}
}
Any help would be greatly appriciated as I have been stuck on this now for days! I know this is a lot of code to paste up here but it is all relevant.
Thanks,
EDIT: The SubscribeToService() method is called when the user logs in and authenticates with the WebAPI. The method is here;
public async Task<User> SendSubmitLogonAsync(LogonObject lo)
{
_logonObject = lo;
using (var client = GetNewHttpClient(false))
{
var logonString = String.Format("grant_type=password&username={0}&password={1}", lo.username, lo.password);
var sc = new StringContent(logonString, Encoding.UTF8);
var response = await client.PostAsync("Token", sc);
if (response.IsSuccessStatusCode)
{
_logonResponse = await response.Content.ReadAsAsync<TokenResponseModel>();
var userInfo = await GetUserInfoAsync();
if (_channel == null)
SubscribeToService();
else
await RegisterForMessageNotificationsAsync();
return userInfo;
}
// ...
}
}
I have solved the issue. There are tons of fairly poorly organised howto's for azure notification hubs and only one of them has this note toward the bottom;
NOTE:
You will not receive the notification when you are still in the app.
To receive a toast notification while the app is active, you must
handle the ShellToastNotificationReceived event.
This is why I was experiencing intermittent results, as i assumed you would still get a notification if you were in the app. And this little note is pretty well hidden.
Have you used proper tag / tag expressions while register/send the message. Also, Where are you storing the id back from the notification hub. It should be used when you update the channel uri (it will expire).
I would suggest to start from scratch.
Ref: http://msdn.microsoft.com/en-us/library/dn530749.aspx

Resources