Is there any way to signin silently without user input in Microsoft.Graph - c#

I am working on a UWP app which use Microsoft.Graph APIs. Every time i close my app then i need to sign in again using my Hotmail credentials for authorization.
I implement this code for GetToken:
private static async Task<string> GetTokenForUserAsync()
{
try
{
authResult = await PCA.AcquireTokenSilentAsync(Scopes);
userToken = authResult.Token;
}
catch (Exception)
{
if (userToken == null || expiration <= DateTimeOffset.UtcNow.AddMinutes(5))
{
authResult = await PCA.AcquireTokenAsync(Scopes);
userToken = authResult.Token;
expiration = authResult.ExpiresOn;
}
}
return userToken;
}
But every time i reopen my app then AcquireTokenSilentAsync() method threw an exception and ultimately i have to go through the sign in process and give my app bunch of permissions. Is there any silent authentication method available which can authorize my app without the signin process.

You seem to be running into a problem with Hotmail's security features and authentication methods. While this maybe convenient for you I would just sign in and deal with the hassle as this creates a lot of security issues. If someone exploited your program they would be automatically signed in imagine the security risks this could pose for you. Never sacrifice security for convenience when coding!

Related

Identity server 4 handling Expired or revoked refresh tokens

I am working with an Identity server 4 system. We are using the exact code from the MvcHybridAutomaticRefresh sample
The issue is with this code here. AutomaticTokenManagementCookieEvents.cs#L73
var response = await _service.RefreshTokenAsync(refreshToken.Value);
if (response.IsError)
{
_logger.LogWarning("Error refreshing token: {error}", response.Error);
return;
}
Currently if a refesh token was revoked by the admins, or the refresh token has expired ( we do not have sliding refresh tokens enabled) Then the application will crash. I would expect it to reroute the user to the login screen.
I am i missing something in this sample that it cant handle that?
I have also posted this as a question on the issue forum #3599
current attempt
is to add The following rather where it detects the error
await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
which i had hoped would log the user out. This just hangs and never goes anywhere. Its not even logging you out of the server.
Current Solution
The only thing i can find currently that remotely works is to add a catch in the api call. This is not ideal as in our actual application we have a lot of api calls this would mean making a lot of changes to our application. Isnt there a way to force a login directly from the middle wear itself?
[Authorize]
public async Task<IActionResult> CallApi()
{
try
{
var token = await HttpContext.GetTokenAsync("access_token");
var client = _httpClientFactory.CreateClient();
client.SetBearerToken(token);
var response = await client.GetStringAsync(Constants.SampleApi + "identity");
ViewBag.Json = JArray.Parse(response).ToString();
return View();
}
catch (Exception)
{
return new SignOutResult(new[] { "Cookies", "oidc" });
}
}
You can add just one row to force the middleware to perform the challenge again:
if (response.IsError)
{
_logger.LogWarning("Error refreshing token: {error}", response.Error);
context.RejectPrincipal();
return;
}

Persist sign-in Microsoft-Graph c# SDK

I'm using Microsoft Graph C#.NET SDK to access user's mail inbox. The problem is that when I do authentication the token that Microsoft sends me back is valid just for 1 hour or so and it expires so early. But it's so annoying for user to login every 1 hours just to see the outlook mail inbox. I need to make this login PERSISTENT.
Here is the code that I use:
public static async Task Run()
{
string secret = "MyDamnPrivateSecret";
PublicClientApplication clientApp = new PublicClientApplication(secret);
GraphServiceClient graphClient = new GraphServiceClient("https://graph.microsoft.com/v1.0", new DelegateAuthenticationProvider(async (requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", await GetTokenAsync(clientApp));
}));
//Processing mailbox ...
}
private static async Task<string> GetTokenAsync(PublicClientApplication clientApp)
{
if (string.IsNullOrEmpty(Properties.Settings.Default.token) || string.IsNullOrWhiteSpace(Properties.Settings.Default.token))
{
//need to pass scope of activity to get token
string[] Scopes = { "User.Read", "Mail.ReadWrite" };
string token = null;
AuthenticationResult authResult = await clientApp.AcquireTokenAsync(Scopes);
token = authResult.AccessToken;
Properties.Settings.Default.token = token;
Properties.Settings.Default.Save();
return token;
}
else
{
return Properties.Settings.Default.token;
}
}
Is there any way to make expiration time last longer? Or make a refresh token or something to persist login?
You'll need to request the offline_access scope to get a refresh token. If you're using an older version of MSAL, you'll need to implement and pass a token cache in the PublicClientApplication constructor which I think that MSAL will use to automatically refresh the access token. I think the newer version handles the tokenCache for you.
From the docs, this is the recommended call pattern: first try to call AcquireTokenSilentAsync, and if it fails with a MsalUiRequiredException, call AcquireTokenAsync.
private static async Task<string> GetTokenAsync(PublicClientApplication clientApp)
{
AuthenticationResult result = null;
try
{
string[] scopes = { "User.Read", "Mail.ReadWrite", "offline_access" };
// Get the token from the cache.
result = await app.AcquireTokenSilentAsync(scopes, clientApp.Users.FirstOrDefault());
return result.AccessToken;
}
catch (MsalUiRequiredException ex)
{
// A MsalUiRequiredException happened on AcquireTokenSilentAsync.
// This indicates you need to call AcquireTokenAsync to acquire a token
System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
try
{
// Dialog opens for user.
result = await app.AcquireTokenAsync(scopes);
return result.AccessToken;
}
catch (MsalException msalex)
{
ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
}
}
catch (Exception ex)
{
ResultText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
return;
}
}
Here's a sample for reference. https://github.com/Azure-Samples/active-directory-dotnet-desktop-msgraph-v2
I will try to clarify the issues here:
MSAL .net is build for different platforms - .net desktop, .net core, UWP, xamarin android and xamarin iOS. On some of these platforms (UWP and xamarin) we persist the token cache for you. On the others, we expect you to persist the cache. The reason is that we cannot provide token serialization logic that works well for all scenarios (e.g. ASP.NET server farms), so we expect you to do it. We provide samples and guidance around it this. Details and some reference implementations on the MSAL wiki:
The sample code provided by #Michael is ok for MSAL v1. In MSAL v2 the things are a bit different and you can find the pattern of calling is also on the MSAL wiki:
We request and store the refresh token (RT). If the auth token (AT) is expired, we will request a new one based on the RT - this will happen without user interaction. This should all be transparent to you, i.e. it should just work :). Make sure that your token cache serialization works, i.e. you get an account when performing
// perform an interactive login first
// otherwise there will be no AT / RT in the store
var accounts = await app.GetAccountsAsync();
// there should be an account that you can use
Most of our samples show how to call the Graph. See all the samples by scenario here. For your use case I recommend you check out Calling the Graph from a WPF app
Also check out #Daniel Dobalian's answer for default expiration of AT and RT:
MSAL token expires after 1 hour
In your code, AcquireTokenAsync does always trigger login.
Instead, you need to implement a token cache and use AcquireTokenSilentAsync.
For more information, please review the following link:
Microsoft Graph SDK - Login

Publishing tweets from C# Windows service using Tweetinvi or similar

I am looking into publishing some service status updates on Twitter using Tweetinvi, which seems like a good library for doing that sort of thing, but I am just starting out looking into this so using it is not set in stone.
However, one thing my research has not yielded yet, is an obvious way to handle Twitter authentication in what is essentially a headless service. I have created an app with Twitter, so I have my consumer key and secret, and I can do the "app only" auth to request user info, get their followers etc., but of course I have no right to publish tweets.
So my ambition is (once this is out of beta) to create a proper twitter account, somehow have the service authenticate towards that account, and then publish status updates from the general service at defined intervals. It is a fairly simple idea.
Of course, I can do something like the PIN based authentication mentioned here:
https://github.com/linvi/tweetinvi/wiki/Authentication
I can run that manually, get the PIN code, and proceed with the workflow. But will this require reauthentication at regular intervals, or will it basically be valid "forever"? I am looking for a way to make this as automatic as possible, and having to redo the auth every x hours is a huge dent in this dream, if not a showstopper.
Of course I will have the password for the twitter account used to publish statuses, but I don't see a way to do a good old fashioned login without manual user intervention - what options do I have?
This behavior is by design. Twitter uses OAuth, which is a protocol with the purpose of allowing a user to authorize an application. This is good for the user because otherwise, you or anyone else can perform actions on their behalf without them knowing.
With that in mind, the only way to do this is to have the user explicitly authorize your app. Here's an example of how to do this with LINQ to Twitter, which I wrote, using ASP.NET MVC. When the user visit's your page, you can have a button that re-directs them to the OAuthController below to the BeginAsync action.
using System;
using System.Configuration;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using LinqToTwitter;
namespace MvcDemo.Controllers
{
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");
}
}
}
After the user authorizes your application, Twitter redirects them back to the CompleteAsync method. Notice the comments on how to extract values from the auth.CredentialStore. Save those in your DB and then retrieve them in your service to make calls on the user's behalf.
Those credentials don't change, but the user can possibly de-authorize your application at some time in the future - at which time you'll need to get them to authorize again. You can get the entire sample code at the LINQ to Twitter ASP.NET Samples page.

Azure App Services Authentication

Has anyone been able to figure out authentication using Azure App Services?
For some strange reason it is no longer handling refresh tokens like it used to in Mobile Services, the token I'm now caching expires in 1 hour, this is useless.
It's a C# UWP app, I'm using Microsoft Account as the login, I've been told to use the OneDrive API to login and retrieve the token and then use that to login to App Services, that doesn't work for me either, with an error like "you do not have permission to access the directory".
Any help is appreciated.
A solution for App Service Mobile, the update to MobileService. There should now be a solution
The code replicated here is:
async Task<string> GetDataAsync()
{
try
{
return await App.MobileService.InvokeApiAsync<string>("values");
}
catch (MobileServiceInvalidOperationException e)
{
if (e.Response.StatusCode != HttpStatusCode.Unauthorized)
{
throw;
}
}
// Calling /.auth/refresh will update the tokens in the token store
// and will also return a new mobile authentication token.
JObject refreshJson = (JObject)await App.MobileService.InvokeApiAsync(
"/.auth/refresh",
HttpMethod.Get,
null);
string newToken = refreshJson["authenticationToken"].Value<string>();
App.MobileService.CurrentUser.MobileServiceAuthenticationToken
= newToken;
return await App.MobileService.InvokeApiAsync<string>("values");
}
Hope it saves somebody time !

Persistent authentication across UWP app and Azure Mobile Service

Building on the example here I'm attempting to authenticate an MSA login on the client, and have it authenticate service-side as well. The difference with mine is I'm using the new WebAccount-related API's in Windows 10 instead of the now deprecated Live SDK.
So far I've got:
var provider = await WebAuthenticationCoreManager.FindAccountProviderAsync("https://login.microsoft.com", "consumers");
var request = new WebTokenRequest(provider, "service::wl.basic wl.emails::DELEGATION", "none");
var result = await WebAuthenticationCoreManager.RequestTokenAsync(request);
if (result.ResponseStatus == WebTokenRequestStatus.Success)
{
string token = result.ResponseData[0].Token;
//This calls my custom wrappers around the Live REST API v5 and runs successfully with this token
var acc = await LiveApi.GetLiveAccount(token);
var jtoken = new JObject
{
{"authenticationToken", token}
};
try
{
//Shouldn't this work? but raises a 401
await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, jtoken);
//Alternate method? Also raises a 401
//await App.MobileService.LoginWithMicrosoftAccountAsync(token);
}
}
As I mentioned in the comments, all I get are 401s.
As far as I can tell the application is configured correctly in Microsoft Account dev center:
I'm using the client ID and secret from the same app in the Azure portal.
JWT issuing is not restricted.
Redirect URL is of the format https://{appname}.azurewebsites.net/.auth/login/microsoftaccount/callback
Authentication works fine when I switch to use purely server-side authentication. i.e.
await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
Any ideas? Am I missing something? Any help would be appreciated.
UPDATED:
The token I get back in the WebTokenRequestResult is 877 characters long and does not appear to be in the JWT format, with the dot (.) separators and I'm quite certain that this is the issue. The following error gets logged in service when the client calls the code above:
JWT validation failed: IDX10708: 'System.IdentityModel.Tokens.JwtSecurityTokenHandler' cannot read this string: 'EwCQAq1DBAAUGCCXc8wU/zFu9QnLdZXy+...Zz9TbuxCowNxsEPPOvXwE='.
Application: The string needs to be in compact JSON format, which is of the form: '<Base64UrlEncodedHeader>.<Base64UrlEndcodedPayload>.<OPTIONAL, Base64UrlEncodedSignature>'..
Application: 2015-12-07T17:47:09 PID[5740] Information Sending response: 401.71 Unauthorized
What format is the token currently in? Can it be transformed to a JWT?
Still no closer to a solution, so any help is appreciated.
Anyone feel free to correct me, but it looks like RequestTokenAsync gets you an access token which you can't use to login the backend. You need an authentication token for that, and as far as I can see RequestTokenAsync doesn't get you that.
There's some info here about the tokens.
If people end up here searching for a solution for App Service Mobile, the update to MobileService. Then there is now a solution
The code replicated here is:
async Task<string> GetDataAsync()
{
try
{
return await App.MobileService.InvokeApiAsync<string>("values");
}
catch (MobileServiceInvalidOperationException e)
{
if (e.Response.StatusCode != HttpStatusCode.Unauthorized)
{
throw;
}
}
// Calling /.auth/refresh will update the tokens in the token store
// and will also return a new mobile authentication token.
JObject refreshJson = (JObject)await App.MobileService.InvokeApiAsync(
"/.auth/refresh",
HttpMethod.Get,
null);
string newToken = refreshJson["authenticationToken"].Value<string>();
App.MobileService.CurrentUser.MobileServiceAuthenticationToken
= newToken;
return await App.MobileService.InvokeApiAsync<string>("values");
}
Hope it saves somebody time !

Categories