Teams Tab for dynamic SharePoint page / authentication problem - azure-active-directory

Heyo,
I am trying to build a multi tenant Teams App with personal tabs. The goal is: the user is authenticated (silently like explained in the Microsoft example, a static html page with the JavaScript code is hosted in an Azure Storage), then my custom web service is called and answers with a SharePoint-Online URL which contains an SPFX Webpart. Then the user is redirected to that page. When the user is not registered he gets an error page instead. The problem here is, that I am not able to redirect the user to that SharePoint page. The SharePoint page won't open in the iframe of the Teams tab because the tab's domain is the domain where my static web page is hosted. So my first question is how can I achieve this? (I do not know the SharePoint URL at build time, i do not know if the user has a page at all. This is determined in the custom web service at runtime)
A different approach would be to build a custom JavaScript application (in react, like my SPFX webpart), host it on my static web page and communicate with the SharePoint from there. To test my idea I added permissions for SharePoint in my Azure AD application, wrote a simple call to the SharePoint with the obtained token.
var req = new XMLHttpRequest();
req.open("GET", "https://[tenant].sharepoint.com/sites/[sitecollection]/_api/web/lists", false);
req.setRequestHeader("Authorization", "Bearer " + idToken);
req.setRequestHeader("Accept", "application/json;odata=verbose");
req.onreadystatechange = function () {
if (req.readyState == 4 && req.status == 200) {
var result = JSON.parse(req.responseText);
}
};
req.send();
The problem is my SharePoint is answering with 401 and "Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown" error in the response.
Is this some sort of CORS problem or do I need a different type of token for SharePoint? Or do I have to use a complete different JavaScript library to authenticate vs SharePoint.

Related

Azure Active Directory - UI > API - 401 Error

Good Day,
Currently I have a single tenent with a React UI and .NET Core Apis secured by Azure Active Directory without any problems.
We have recently moved to a new Azure Tenent, new Active Directory etc. I have create two new App Registrations, one single App Service for UI and one for API. I have linked the App Service to AAD (UI = UI App Registration, API = API App Registration).
The problem is the API is getting a 401 error and I think see that in the original tenent the Bearer token is in a JWT format but in the new instance it's not, I believe it my be a graph api access key.
New Tenent:
Authorization: Bearer PAQABAAAAAAD--DLA3VO7QrddgJg7WevrQvEQVbZEMD8su-tIp9k2bTFUTort7SZgeDI52P6KRYefHgtmj4YrecgUKZJ2wylGuhvIzIz642n7Sg0VMU1RwKtrzWlaMqK62CaSoJcstxiEf6 *****
Orginal Tenent:
Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyIsImtpZCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyJ9.eyJhdWQiOiI3OThkN2ZkOC0zODk2LTQxOGMtOTQ0Ny0wNGFlNTQ2OGFkNDIiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83ZDE3NTU3Ni03Y2Y3LTQyMDctOTA5My0wNmNiNmQyZDIwNjAvIiwiaWF0IjoxNjE2NDUyNzExLCJuYmYiOjE2MTY0NTI3MTEsImV4cCI6MTYxNjQ1NjYxMSwiYWNyIjoiMSIsImFpbyI6IkFTUUEyLzhUQUFBQU9mejhPZHp *****
Please someone kindly enought to provide some guidance / input where I am going wrong.
Regards
Paul.
When using Azure AD to obtain an access token, an additional resource parameter is required. Otherwise, the access token is not a JWT.
For example, if your web API's application ID URI is https://contoso.com/api and the scope name is Employees.Read.All, then with oidc-client the client configuration should be :
scope: 'openid profile email Employees.Read.All',
extraQueryParams: {
resource: 'https://contoso.com/api'
}
In App Service auth configuration, you can use additionalLoginParams
"additionalLoginParams": ["response_type=code", "resource=https://contoso.com/api"]
If you did not use a custom application ID URI, it may look like
api://868662dd-3e28-4c7f-b7d5-7ec02ac9c601
Quickstart: Configure an application to expose a web API
Firstly, the scope is incorrect.
You should Expose an API in your API App Registration and then add it as a permission in your UI App Registration. You can refer to this document.
And when you try to call the 'https://login.windows.net/{tenant}/oauth2/authorize endpoint, you need to specify the scope to include api://{app id of the API App Registration}. For example: api://{app id of the API App Registration} openid profile email. Then the access token would be for calling your API.
At last, for CORS issue, please configure the CORS as * in your web app to see if it helps.
Try to follow this step: Configure App Service to return a usable access token
In my experience, this problem occurs, when you try to authorize against version 1 of the endpoint.
Instead of calling
https://login.microsoftonline.com/{tenant}/oauth2/authorize
call
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize
You might be required to set something like "metadata URL" in you authorization library to:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/.well-known/openid-configuration
Make sure your builder follows this order...lifted from our API program.cs
These must be in order of
UseRouting -> UseAuthentication -> UseAuthorisation -> MapControllers
> app.UseRouting()
> app.UseAuthentication()
> app.UseAuthorization()
> app.MapControllers()
If app.UseAuthentication and app.UseAuthorization are not in this order in statement position you Will get 401 Unauthorised as at 01/2023 .Net 6 Core.

ADAL.JS with Mobile App

I'm trying to integrate some Oracle delivered Mobile Application Framework Apps (MAF) mobile apps with Azure AD authentication. I have tried the Java approach, which apparently doesn't work in my case.
So I decided to try using a Javascript login page option using ADAL.JS. Since MAF creates cross-platform compatible code by transpiling to HTML 5/Javascript/Cordova, I reckoned I could make the JS option work without resorting to having multiple SDK specific solutions like ADAL-Android or ADAL-IOS. Since I can wrap it all in an HTML page as I can use the OAUTH implicit flow option that ADAL.JS requires. I have the ADAL.JS part working from my PC using this example with a local Node/Webpack dev server for the redirect URI. (Note, just like that example, I'd prefer to use the strict adal.js option and avoid any angular-js stuff). However, I'm running into an issue when deployed on the Android mobile device. It appears to be due to the reply URI. After being prompted for Azure credentials and supplying those, the following error is produced.
AADSTS50011: Reply address 'file:///data/user/0/com.company.app/storage/assets/FARs/ViewController/public_html/SignOn/login.html' has an invalid scheme.
I found that when deploying to a mobile device the Azure registered app must be set to type "Native" instead of "Web/API" which I have done. And according to an MSFT example (which I cannot include since I don't have enough rep to include more than two links) the redirect URI must be set to "https://login.microsoftonline.com/common/oauth2/nativeclient". But I still get the same error.
UPDATE since #FeiXue Reply
I'm using the original endpoint not 2.0. When I set the redirectURI as such:
redirectURI=https://login.microsoftonline.com/common/oauth2/nativeclient
The browser returns this in the address bar and remains there on a blank screen and does not issue a token. It does this both on the PC browser and mobile browser.
http://login.microsoftonline.com/common/oauth2/nativeclient#id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImEzUU4wQlpTN3M0bk4tQmRyamJGMFlfTGRNTSIsImtpZCI6ImEzUU4wQlpTN3M0bk4tQmRyamJGMFlfTGRNTSJ9.(shortened for brevity)&state=e1ce94fb-6310-4dec-9e8b-053727ceb9b8&session_state=1beafa4d-af55-415b-85d5-83e8b4035594
However, for the exact same code, on the PC when I set the redirectURI as such it returns an access token:
redirectURI=https://localhost:8443 <-- port to my local node server
I've also tried it with a redirectURI of urn:ietf:wg:oauth:2.0:oob, but that does not work either.
Code
<!DOCTYPE html>
<html>
<head>
<title>Authenticate User with ADAL JS</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal.js"></script>
<script type="text/javascript">
$(document).ready(function() {
"use strict";
var variables = {
azureAD: "mytenant.onmicrosoft.com",
clientId: "cc8ed7e0-56e9-45c9-b01e-xxxxxxxxxx"
}
window.config = {
tenant: variables.azureAD,
clientId: variables.clientId,
postLogoutRedirectUri: window.location.origin,
redirectUri: "https://login.microsoftonline.com/common/oauth2/nativeclient",
endpoints: {
aisApiUri: "cc8ed7e0-56e9-45c9-b01e-xxxxxxxxxx"
}
//cacheLocation: "localStorage"
};
var authContext = new AuthenticationContext(config);
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
if (isCallback && !authContext.getLoginError()) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
var user = authContext.getCachedUser();
if (!user) {
authContext.login();
}
authContext.acquireToken(config.endpoints.aisApiUri, function (error, token) {
if (error || !token) {
console.log("ADAL error occurred in acquireToken: " + error);
return;
}
else {
var accessToken = "Authorization:" + " Bearer " + token;
console.log("SUCCESSFULLY FETCHED TOKEN: " + accessToken);
}
});
});
</script>
</head>
<body>
<h1>Test Login</h1>
</body>
</html>
Update
#FeiXue So I guess from what you're saying the id_token IS the access token? I think then the problem is this.
When the redirectURI="https://localhost:8443" it redirects back to my index.html after AAD login and the authContext.acquireToken() works and returns a valid token.
But when the redirectURI="https://login.microsoftonline.com/common/oauth2/nativeclient" it never redirects back from http://login.microsoftonline.com/common/oauth2/nativeclient#id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni......
While it shows the id_token, it never redirects back to my index.html So I can't make a call to authContext.acquireToken() for passing it onto my web API.
From my research on this topic here is the gist on ADAL.JS and Native (Mobile) Device Support
As #fei-xue-msft mentioned, ADAL.JS is not intended for nor does it work with native/mobile devices. ADAL.JS was written with the “original” Azure endpoint in mind, not the v2.0 endpoint that provides more functionality for mobile/native devices (see more below on the two different endpoint options). There is however an experimental ADAL.JS branch you can try (uses the v2.0 endpoint), but it is not not being actively updated anymore so you are on your own. The new MSFT approach is to use the new MSAL library, which is written towards the v2.0 endpoints. However there is no MSAL-for-JS library yet but rumor is there will be one at some point. For more on the two different Azure endpoints (“original” versus “v2.0”) see the links below. The confusion over this was a source of frustration in my troubleshooting so I help this helps some others going down this track.
So if you are looking to get Azure Oauth authentication on mobile devices, first decide which Azure Endpoint you want to use (Supporting links on that below as v2.0 does have some restrictions that the original endpoint does not). You can determine what your specific endpoints for your tenant are by viewing the Metadata Doc links listed below, just substitute your tenant name or ID. You should be able to use either.
To register an application for a specific type of endpoint (original versus v2.0) use the appropriate App Registration Portal link cited below. Then, to decide what your options are for creating an Azure auth solution for native/mobile device, see the code samples for each endpoint version, and make sure the sample is for “native” else it probably won’t work on your mobile device. For example, you will not see an ADAL.JS sample for the original endpoint library options, but you will see one for Cordova (which is why #fei-xue-msft suggested that approach). For the v2.0 endpoint samples you will see the MSAL/Xamarin options, and for an Javascript option you can try something like the Hello.JS Sample.
Original Endpoint
https://login.microsoft.com/{tenant id}/oauth2/authorize
App Registration Portal: https://portal.azure.com
Code Samples: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-code-samples#native-application-to-web-api
Native Auth Scenarios: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-scenarios#native-application-to-web-api
OpenID Metadata Doc: https://login.microsoft.com/{tenant id}/.well-known/openid-configuration
V2.0 Endpoint
https://login.microsoftonline.com/{tenant id}/oauth2/v2.0/authorize
App Registration Portal: https://apps.dev.microsoft.com
V2.0 Endpoint Compare: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-compare
Code Sample: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-libraries
OpenID Metadata Doc: https://login.microsoft.com/{tenant id}/v2.0/.well-known/openid-configuration
Are you developing with Azure AD V2.0 endpoint?
If not, we are able to config the redirect URIs as we want on the portal for the native app. However as the error message indicates that the file protocol is not a a validate scheme.
In this scenario, we can use the http or https since you were developing with HTML.
And in the Azure AD V2.0 endpoint, we are not able to set the redirect_Uri for the native app at present. We can use urn:ietf:wg:oauth:2.0:oob or https://login.microsoftonline.com/common/oauth2/nativeclient for the redirect_Uri. The first one is used for the native app for the device and the second we can use for the client which host in browser(web-view).
At last, please ensure that the redirect_uri in the request is using the correct one you register for the portal. You can also test the request on the browser to narrow down whether this issue was cause the incorrect redirect_uri in the request. And for the authorization request, you can refer links below:
Authorize access to web applications using OAuth 2.0 and Azure Active Directory
v2.0 Protocols - OAuth 2.0 Authorization Code Flow
Update(there is no href property if open the HTML from disk which cause the popup page is not closed)
AuthenticationContext.prototype._loginPopup = function (urlNavigate) {
var popupWindow = this._openPopup(urlNavigate, "login", this.CONSTANTS.POPUP_WIDTH, this.CONSTANTS.POPUP_HEIGHT);
if (popupWindow == null) {
this.warn('Popup Window is null. This can happen if you are using IE');
this._saveItem(this.CONSTANTS.STORAGE.ERROR, 'Error opening popup');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, 'Popup Window is null. This can happen if you are using IE');
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'Popup Window is null. This can happen if you are using IE');
if (this.callback)
this.callback(this._getItem(this.CONSTANTS.STORAGE.LOGIN_ERROR), null, this._getItem(this.CONSTANTS.STORAGE.ERROR));
return;
}
if (this.config.redirectUri.indexOf('#') != -1)
var registeredRedirectUri = this.config.redirectUri.split("#")[0];
else
var registeredRedirectUri = this.config.redirectUri;
var that = this;
var pollTimer = window.setInterval(function () {
if (!popupWindow || popupWindow.closed || popupWindow.closed === undefined) {
that._loginInProgress = false;
window.clearInterval(pollTimer);
}
try {
//there is no href property if open the HTML from disk
if (popupWindow.location.href.indexOf(registeredRedirectUri) != -1) {
if (that.isAngular) {
that._onPopUpHashChanged(popupWindow.location.hash);
}
else {
that.handleWindowCallback(popupWindow.location.hash);
}
window.clearInterval(pollTimer);
that._loginInProgress = false;
that.info("Closing popup window");
popupWindow.close();
}
} catch (e) {
}
}, 20);
};
This issue is caused that when we open the HTML page from device(disk), the parent HTML page(login page) is not able to get the location of the popup page. So the parent page is not able to close that page based on the location of popup page. To workaround this issue, I suggest that you developing with azure-activedirectory-library-for-cordova or host the login page on the back end of web API.

Preventing Direct URL to views in angular + firebase web app

I'm not an expert as I started working in firebase and angularjs a few weeks ago. I'm building a server based web application using firebase, nodejs(for server) and angularjs and I've a login scenario for my web application, Now I want to prevent direct URL access, after checking that a user is login or not. I want to redirect it to login page if user try to access via direct URL.
I tried using this but it doesn't seems to work.
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
Can some provide me a working example of preventing access to URLs using angularjs and firebase?
Thanks in Advances

Owin OpenIdConnect Active Directory HttpContext.GetOwinContext doesn't open microsoftonlin login page

I'm trying to use Owin and OpenIdConnect to authenticate users via active directory (office 365 online). I've followed this example and I managed to create a new MVC test project and get it all working. (Settings for AD app id, tenant, Web config etc all fine).
I'm now trying to add that functionality into my existing ASP.net mvc application and I can't get the dang thing to work.
This is what I have: An Account Controller with a "void" action like this (from the example that works in my PoC but not in my actual application):
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
When this action is invoked, I expect the browser to be directed to: login.microsoftonline.com..., but instead it opens this page: https://localhost:44301/Account/Login?ReturnUrl=%2fAccount%2fSignIn
It's like it's calling some sort of redirect somewhere and I can't see where.
Help!
I found the answer. I had to do 2 things:
Remove the WebMatrix dll's from the references (apparently nuget package for mvc put it there, so it might come back)
Remove authentication mode="Forms" from web.config
Thanks.

API on subdomain and oauth

I have an api on a subdomain : api.exemple.com written with symfony2 and my main application on exemple.com (SPA - AngularJs).
I would like to allow user to link their facebook account with their local account. I don't know how to proceed in order to authenticate through my app and use third party oauth provider.
Do you have any clue ?
Thank you
On the angular side, start by opening a new window and send your oauth handler a get request:
self.oauthConnect = function(provider)
{
var url = apiPrefix + '/oauth/tokens/'. provider;
oauthWindow = $window.open(url,'_blank', 'height=600, width=600, top=100, left=300, modal=yes');
oauthWindow.focus();
};
Your PHP api site then redirects to the oauth provider site (i.e. facebook). We use a new client window so our SPA keeps running in spite of the redirect. The provider then presents their login screen and redirects with the oauth token information.
Your PHP api site does what it needs to and generates the actual authorization token (hint: use a json web token). The site then returns an html page back to your angular app.
<body>
<script>
window.opener.oauthCallback('<?php echo $oauthToken; ?>');
</script>
</body>
Your angular controller (that opened the window) will then be called with the oauth token.
$window.oauthCallback = function(oauthToken)
{
oauthWindow.close();
oauthWindow = null;
authManager.oauthToken = oauthToken;
self.oauthSubmit();
};
Easy right? Well not really but it works. In my case I turn right around and:
POST /auth/tokens/oauthToken
to get the real application token. That way my oauth service can be used for multiple applications.

Resources