I have a Blazor Server App that uses Microsoft Identity with local user accounts. It works perfectly; however, I want to extend the app to have access to Office 365, namely mail, calendar and a sharepoint document library. This can be done via Microsoft Graph, but all the documentation points to use Microsoft Identity with Work or School accounts instead of local user accounts. I do not want to change the Blazor app since I will add also support for other cloud services. The main question is can I leave my Blazor app with Microsoft Identity and use MSAL or something else with "on behalf of user" access in addition, capturing or asking the user to enter, once, his/her credentials for office 365 and thus gain access?
The goal is to have the blazor server side app running with local account and prompt once for the user's Office365 login, get the access and retrieve the data. Next time the user logs into the app, he won't need to login to Office365 for the app to have access to the email, calendar and a sharepoint site as the user.
After battling with ADAL, MSAL, and other wrappers for OAUT2 authentication for Azure, I came to a simple solution which reminded me of the KISS (Keep It Simple S!"#$%) aspect. Sometimes we overthink the problems and it was my situation.
After registering the application in Azure AD, make sure your redirect URI matches an application page, component, or method of capturing the authentication code. I used the following code (this is in a Razor component since I use a Blazor Server App):
string AuthUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
string ReqUrl = AuthUrl + "?scope=";
ReqUrl += string.Join("+", scopes);
ReqUrl += "&response_type=code";
ReqUrl += "&client_id=" + [Replace these brackets with your Client ID];
ReqUrl += "&redirect_uri=[Replace these brackets with your redirect URI for your app page]";
ReqUrl += "&prompt=consent";
ReqUrl += "&response_mode=query";
NavManager.NavigateTo(ReqUrl);
Since this code navigates to the Azure AD Authentication, if successfully authenticated it redirects to another razor component in my Blazor Server App to capture the authorization code in the query string which can be easily extracted using the System.Web code below:
QueryHelpers.ParseQuery(uri.Query).TryGetValue("code", out [Replace with Code Variable])
You can then acquire an access token, simply again using the same process as authentication, with Post instead of Get:
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", [Replace with CLient ID]),
new KeyValuePair<string, string>("scope", string.Join(" ", scopes)),
new KeyValuePair<string, string>("grant_type", "[Insert Authorization Code you got in the query string]"),
new KeyValuePair<string, string>("code", HttpUtility.UrlEncode([Replace with Authentication Code])),
new KeyValuePair<string, string>("redirect_uri", "[Redirect URI from App Registration]")
}
);
HttpResponseMessage response = await client.PostAsync("/common/oauth2/v2.0/token", content);
var str = response.Content.ReadAsStringAsync().Result;
Another important aspect of this access code PostAsync is that scopes are delimited with a space and NOT a plus "+" sign as in authentication.
From here, the only caveat of not using a library, is that you must implement your own token cache and renewal. The response from the access token acquisition will return the access token, expiration date and refresh token. I used this Archived Lab Manual of how to get an access token to guide me and to replicate it in C#/Blazor.
I hope this helps anyone else that has been wanting to achieve the same goal, that is to have Local Accounts in a Blazor Server App that is linked to an Office 365 account for access to mail and other resources through MS Graph. Feel free to send a message if anything needs further clarification.
Happy Coding!
Yes, I came to a similar conclusion and implemented an almost identical solution - wish I had seen this post first though.
Pretty disappointing that Microsoft.Identity.Web doesn't seem to support Individual Accounts - I felt a bit exposed having to implement something as important as authentication effectively from first principles.
There remain challenges with this approach if you want to move to Blazor WASM. The secure caching of the access token has been my concern on client side. I wasted a lot of time trying to work out how to integrate Azure authentication using current libraries with Indiv Accounts. (Note I wanted to make this a comment, but I don't have enough reputation on SO)
Related
I'm struggling to correctly configure my app in the Azure portal. My application prompts for user permission every time it runs. The goal is to prompt an admin user ONE TIME to grant permissions to my app.
I'm writing a CONSOLE application to create a new task in Microsoft Planner by making API calls to the Microsoft Graph API.
I'm using delegated permissions for this so this application needs to take on the permissions of a user.
The good news is I've gotten this to work already.
What I need help with is configuring my app in Azure portal correctly.
Right now, every time I run the app, it prompts for consent. I'd like the app to ask an administrator to grant permissions to the app up front once and let the console app work without prompting users after that.
One issue I have is setting up the Redirect URI. The ONLY way I've gotten this to work so far is to set the URI to "urn:ietf:wg:oauth:2.0:oob".
As much as I've read about redirect URIs, you'd think I'd have this part figured out. I've tried using:
https://login.microsoftonline.com/{0} ({0} = tenant id
msal423d50b9-dfba-4c66-88f8-26c1432083e4://auth
https://login.microsoftonline.com/common/oauth2/nativeclient
https://login.live.com/oauth20_desktop.srf
When my console application runs, it directs the user to the login.microsoftonline.com and there I can choose a user, and then it asks me if I'd like to grant authority on behalf of my organization to all the permissions listed. I click the Accept button and it tells me that the Redirect URI does not match the one in my configuration.
Keeping in mind this is a console application, can someone please advise me as to how I should configure this to work correctly?
private static IAuthenticationProvider CreateAuthorizationProvider(string clientId, string authority, IEnumerable<string> scopes)
{
var clientApplication = new PublicClientApplication(clientId, authority);
return new MsalAuthenticationProvider(clientApplication, scopes.ToArray());
}
As you can see, the code is passing a client Id and authority. The authority in this case is where I'm passing the redirect URI. This is where I believe my problem is and where I could really use some help.
Every example out there is for a web app of some sort.
I am adding IdentityServer3 on to an existing website (NopCommerce to be specific). It has it's own registration and authentication system, but we need to also offer OpenID Connect so that a back end application can be integrated. Calls to the back end need to have an id_token for the current user that the back end validates to confirm the identity.
I've found information about how to use an existing membership database to provide the user data for IdentityServer3 to check, however I am bit stuck on how to generate the id_token for each user. I guess the obvious answer is to replace the website login with IdentityServer, however that raises further issues for the rest of the project. Ideally I'd like the user to log in as normal and then call a method in IdentityServer to generate the id_token.
Is this possible? I've been hunting around, but can't find anything so far. The best I found was an answer to programmatically sign in to identityserver3. I think it's suggesting making a HTTP post to IdentityServer, but it feels kind of hacky.
I also found Implementing OAuth 2.0 and OpenId Connect provider using IdentityServer3 with existing login server and membership provider, but I have to admit it's assuming quite a bit of knowledge I don't have (yet).
My solution based on John C's answer worked using a NopCommece external authentication plugin, but I was unhappy with having to leave Nop to authenticate and register when IdentityServer was using the Nop database. Going via the external auth route seemed to be the only way to get an OpenID Connect id_token. After a break and some time to revisit the code though I found the following:
https://identityserver.github.io/Documentation/docsv2/configuration/serviceFactory.html
https://identityserver.github.io/Documentation/docsv2/configuration/serviceFactory.html
By implementing custom services, IdentityServer allows you to mess with the token creation and generation AND the dependency injection system it uses gives you access to instatiated versions of the default services.
Previously I had followed an answer which sent a username and password to the token endpoint. The OpenID specs say that this should only return the access_token, which is exactly what DefaultTokenService in IdenttyService does. By adding a CustomTokenResponseGenerator however, I was able to re-use the request to create and return an id_token too.
CustomTokenResponse class:
internal class CustomTokenResponseGenerator : ICustomTokenResponseGenerator
{
protected ITokenService _tokenService;
public CustomTokenResponseGenerator(ITokenService tokenService)
{
_tokenService = tokenService;
}
public Task<TokenResponse> GenerateAsync(ValidatedTokenRequest request, TokenResponse response)
{
var tokenRequest = new TokenCreationRequest
{
Subject = request.Subject,
Client = request.Client,
Scopes = request.ValidatedScopes.GrantedScopes,
//Nonce = request.AuthorizationCode.Nonce,
ValidatedRequest = request
};
var idToken = _tokenService.CreateIdentityTokenAsync(tokenRequest);
idToken.Wait();
var jwt = _tokenService.CreateSecurityTokenAsync(idToken.Result);
response.IdentityToken = jwt.Result;
return Task.FromResult(response);
}
}
How to inject the custom service in Startup.cs:
factory.TokenService = new Registration<ITokenService, TokenService>();
factory.CustomTokenResponseGenerator = new Registration<ICustomTokenResponseGenerator, CustomTokenResponseGenerator>();
When user logs-in into NopCommerce application, you can send an HTTP authorize request to identityserver. Make sure while sending the authorize request to idsrv you are using prompt=none, this way you will get the id_token or access_token without showing a consent to the user again if the user is already logged-in.
function getAuthorizeRequest() {
var url = global.appSettings.identityServerURL
+ "/connect/authorize?client_id=siteB&response_type=id_token token&redirect_uri="
+ global.appSettings.siteBUrl + "/Main/NopCommerceapp&scope=siteBscope openid email roles&prompt=none&nonce="76767xz676xzc76xz7c67x6c76"
return encodeURI(url);}
Checkout idsrv authorize endpoint https://identityserver.github.io/Documentation/docsv2/endpoints/authorization.html
I think your best solution would be to implement IdentityServer3 and get it reading from your existing Nop membership database. Then create a Web API app that runs on its own that utilizes your IdentityServer3 setup. Inside the Web API, you implement all the functionality that your back end app needs, reading and writing to the Nop database.
Keep your Nop frontend UI separate from your backend API. If you follow the two links below, you should be able to get something up and running pretty quickly.
Creating the simplest OAuth2 Authorization Server, Client and API
MVC Authentication & Web APIs
TL;DR When using google oauth on desktop app, what to save on disk to avoid repeated sign in? Save the google user id? or the token? or an session id?
I'm creating an little desktop app, whitch must authenticate to my REST API server. I'm using google oauth2 for that.
The idea is, that when the desktop app will be authentivated, it generates some data that will be send to my server. The server will store the data with the google user id received from https://www.googleapis.com/userinfo/v2/me.
On the first run of the desktop app, it will open the default browser, with and url for my server and start an local http server. then:
my server will redirect the browser to google (with the clientid, secret, etc.)
user logs in and it will be redirected back to the server with the oauth code
server uses the code to get the token, and then the user profile and stores the token and the profile in db, then redirects the browser to localhost with an paramerer
the desktop app catches the parameter and stores it in an file on the disk
next time the desktop app will start it only reads the file for the parameter to send the generated data with it to my server
my question is: what the parameter should be? the google user id? the oauth token? an generated session id for this desktop app? or something else?
when it will be the google user id, it can conveniently sent the data with the user id and the rest server will just store it in db as is. but I don't think it's safe
when it will be the token, the rest server has to with every request also get the user profile from google with the token. and imho sending the token with every request isn't safe either
generating an session id means to store it with the user and the token on the server and the desktop app will just store it and send it with every request. but I don't know if it's safe to do that
As it's normally the case in software development you have a couple of options depending on requirements.
The mandatory requirement is that your client (desktop) application needs to send something to your REST API so that the API can perform up to two decisions:
Decide who the user is.
Decide if the user is authorized to perform the currently requested action.
The second step may not be applicable if all authenticated users have access to exactly the same set of actions so I'll cover both scenarios.
Also note that, for the first step, sending the Google user ID is not a valid option as that information can be obtained by other parties and does not ensure that the user did authenticate to use your application.
Option 1 - Authentication without fine-grained authorization
Either always sending the id_token or exchanging that token with your custom session identifier both meet the previous requirement, because the id_token contains an audience that clearly indicates the user authenticated to use your application and the session identifier is generated by your application so it can also ensure that. The requests to your API need to use HTTPS, otherwise it will be too easy for the token or session ID to be captured by an attacker.
If you go with the id_token alternative you need to take in consideration that the token will expire; for this, a few options again:
repeat the authentication process another time; if the user still has a session it will indeed be quicker, but you still have to open a browser, local server and repeat the whole steps.
request offline_access when doing the first authentication.
With the last option you should get a refresh token that would allow for your application to have a way to identify the user even after the first id_token expires. I say should, because Google seems to do things a bit different than the specification, for example, the way to obtain the refresh token is by providing access_type=offline instead of the offline_access from OpenID Connect.
Personally, I would go with the session identifier as you'll have more control over lifetime and it may also be simpler.
Option 2 - Authentication + fine-grained authorization
If you need a fine-grained authorization system for your REST API then the best approach would be to authenticate your users with Google, but then have an OAuth 2.0 compliant authorization server that would issue access tokens specific for your API.
For the authorization server implementation, you could either:
Implement it yourself or leverage open source components
⤷ may be time consuming, complex and mitigation of security risks would all fall on you
Use a third-party OAuth 2.0 as a servive authorization provider like Auth0
⤷ easy to get started, depending on amount of usage (the free plan on Auth0 goes up to 7000 users) it will cost you money instead of time
Disclosure: I work at Auth0.
There should be no problem sending the access_token with every request since they are created for that purpose and are thus short lived. You can use the Google Authorization Server endpoint to verify a token instead of using it to do a request for a users profile.
If you're only relying on Google for authentication, here's how your workflow can look:
the client (desktop application, in your case) retrieves the
Google id_token following the user's log in, and then sends it to
the server
the server validates the integrity of said token and extracts the user's profile data; this could mean a simple GET on Google's endpoint to verify this token: https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}
On subsequent requests, nothing should change really, except that the user's login process will be automated (since he's given permissions & all), and thus much faster. #danielx is right, there's no problem with sending the token each and every time.
I've been reading a lot about the topic but all I find are obsolete or partial answers, which don't really help me that much and actually just confused me more.
I'm writing a Rest API (Node+Express+MongoDB) that is accessed by a web app (hosted on the same domain than the API) and an Android app.
I want the API to be accessed only by my applications and only by authorized users.
I also want the users to be able to signup and login only using their Facebook account, and I need to be able to access some basic info like name, profile pic and email.
A possible scenario that I have in mind is:
The user logs-in on the web app using Facebook, the app is granted
permission to access the user Facebook information and receives an
access token.
The web app asks the API to confirm that this user
is actually registered on our system, sending the email and the
token received by Facebook.
The API verifies that the user
exists, it stores into the DB (or Redis) the username, the token and
a timestamp and then goes back to the client app.
Each time the
client app hits one of the API endpoint, it will have to provide the
username and the token, other than other info.
The API each time
verifies that the provided pair username/token matches the most
recent pair username/token stored into the DB (using the timestamp
to order), and that no more than 1 hour has passed since we stored
these info (again using the timestamp). If that's the case, the API
will process the request, otherwise will issue a 401 Unauthorized
response.
Does this make sense?
Does this approach have any macroscopic security hole that I'm missing?
One problem I see using MongoDB to store these info is that the collection will quickly become bloated with old tokens.
In this sense I think it would be best to use Redis with an expire policy of 1 hour so that old info will be automatically removed by Redis.
I think the better solution would be this:
Login via Facebook
Pass the Facebook AccessToken to the server (over SSL for the
android app, and for the web app just have it redirect to an API endpoint
after FB login)
Check the fb_access_token given, make sure its valid. Get user_id,email and cross-reference this with existing users to
see if its a new or old one.
Now, create a random, separate api_access_token that you give back to the webapp and android app. If you need Facebook for
anything other than login, store that fb_access_token and in your
DB associate it with the new api_access_token and your user_id.
For every call hereafter, send api_access_token to authenticate it. If you need the fb_access_token for getting more info, you can
do so by retrieving it from the DB.
In summary: Whenever you can, avoid passing the fb_access_token. If the api_access_token is compromised, you have more control to see who the attacker is, what they're doing etc than if they were to get ahold of the fb_access_token. You also have more control over settings an expiration date, extending fb_access_tokens, etc
Just make sure whenever you pass a access_token of any sort via HTTP, use SSL.
I know I'm late to the party, but I'd like to add a visual representation of this process as I'm dealing with this problem right now (specifically in dealing with the communication between the mobile app and the web api by securing it with a 3rd party provider like facebook).
For simplicity, I haven't included error checks, this is mostly just to outline a reasonable approach. Also for simplicity, I haven't included Tommy's suggestion to only pass your own custom api token once the authorization flow is over, although I agree that this is probably a good approach.
Please feel free to criticize this approach though, and I'll update as necessary.
Also, in this scenario, "My App" refers to a mobile application.
I have the following situation:
I'm currently attempting to write a Metro-style application, with the ability to let the user sign in with his Google account, and the app requestion several permissions, as Userinfo.profile and Userinfo.email.
For this I'm currently using OAuthv2, and requesting the details worked out fine (after quite some effort and research).
Url used :
const string url = "https://accounts.google.com/o/oauth2/auth?" +
"client_id=" + clientId +
"&redirect_uri=" + "urn:ietf:wg:oauth:2.0:oob" +
"&response_type=code" +
"&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email" +
"&access_type=offline";
Now the problem is, if I want the user to stay authenticated, I'd either have to do it by saving the token at the clientside, by the user, or find some way to log in using Google, keep the session and check if he is authenticated.
Problem is, I don't know how to perform the latter, and have no idea how to use it.
At the moment, every time I open the application, the user is redirected to the google authorization page, where the whole procedure starts over and over again at each restart.
What I want to accomplish is that if a user logs in, I have some way of identifying him, and not always put a burden on him by re-requesting permissions for the given details.
I looked into OpenID, but I always get
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://specs.openid.net/auth/2.0/server</Type>
<Type>http://openid.net/srv/ax/1.0</Type>
<Type>http://specs.openid.net/extensions/ui/1.0/mode/popup</Type>
<Type>http://specs.openid.net/extensions/ui/1.0/icon</Type>
<Type>http://specs.openid.net/extensions/pape/1.0</Type>
<URI>https://www.google.com/accounts/o8/ud</URI>
</Service>
</XRD>
</xrds:XRDS>
As a response on this url :
https://www.google.com/accounts/o8/id?openid.mode=checkid_setup&openid.ns=http://specs.openid.net/auth/2.0&openid.return_to=urn:ietf:wg:oauth:2.0:oob&openid.ns.ui=http://specs.openid.net/extensions/ui/1.0&openid.ns.ax=http://openid.net/srv/ax/1.0&openid.ax.mode=fetch_request&openid.ax.required=email,firstname,language,lastname,country&openid.ns.ext=http://specs.openid.net/extensions/oauth/1.0&openid.ext2.consumer=https://www.google.com/accounts/o8/ud?openid.mode=checkid_setup&openid.ns=http://specs.openid.net/auth/2.0&openid.return_to=urn:ietf:wg:oauth:2.0:oob&openid.ext2.scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email
Am I building it wrong ?
If any suggestions, on either how to perform this 'correctly', what I'm trying to accomplish (an integration with google, allowing the user log in using google email, and only if he hasn't authorized the application, ask him to do so, plus finding a way of who he is when logging in for a second+ time), or on how I could do it, please feel free.
Besides, by using the stripped down version of the .Net 4.5 framework, it seems I'm unable to use OpenID libraries as they rely on the full 4.5 .
*Edit* Looking at MSDN this comes up :
To support SSO, the online provider must allow you to register a redirect URI in the form ms-app://appSID, where appSID is the SID for your app.
Does this mean it just isn't possible? Because at google I can only request a key for a domain with http(s):// ?
Have you looked at this? https://developers.google.com/accounts/docs/OAuth2Login
It looks like you are using the Webserver flow and are requesting offline access. This should give you a refresh token after you swap the code. Hold onto this token in your application. If you do an authorization for login as well, you'll get a token back and you can use that to get the userid of the user at Google
Your application should keep track if you have a valid refresh token for a particular user. Then use that token to get the current credentials. You can use tokeninfo enpoint to validate the token. If you have a valid token for the user, there is no need to send them through the code flow to get another refresh token.
If you use the tokeninfo endpoint, the userid field is only present if the https://www.googleapis.com/auth/userinfo.profile scope was present in the request for the access token. The value of this field is an immutable identifier for the logged-in user. Store this and you should have a durable identifier of the user.