how to get Facebook profile picture using Azure AD B2C - facebook

I am using MSAL.js and could successfully sign-in/sign-up users in Azure AD B2C using Facebook as identity provider. The problem is that after sign-in I cannot retrieve user's profile picture.
Azure AD B2C returns an object identifier which has no tie to user's Facebook id.

Using custom policies, you can retrieve the picture field for the Facebook user and then issue a picture claim in the ID token, as follows.
1: Complete the Azure Active Directory B2C: Get started with custom policies steps with one of the social account policies such as the SocialAndLocalAccounts one.
2: Declare a "picture" claim in the extensions file:
<ClaimType Id="picture">
<DisplayName>Picture</DisplayName>
<DataType>string</DataType>
</ClaimType>
3: Add both the "picture" field to the "ClaimsEndpoint" metadata item and the "picture" output claim to the "Facebook-OAUTH" technical profile in the extensions policy:
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<Metadata>
<Item Key="client_id">facebook_clientid</Item>
<Item Key="scope">email public_profile</Item>
<Item Key="ClaimsEndpoint">https://graph.facebook.com/me?fields=id,first_name,last_name,name,email,picture</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="picture" PartnerClaimType="picture" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
4: Issue the "picture" claim in the sign-up or sign-in relying party policy:
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="picture" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>

Related

Sharepoint 365 Application does not have administrative permissions in tenant

I have an application created in my tenant that has the following app permissions granted and trusted.
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="FullControl" />
</AppPermissionRequests>
Yet when I try and use the REST API to do a KQL search (Keyword Query Language) I get the following error
<?xml version="1.0" encoding="utf-8"?>
<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code>-1, Microsoft.Office.Server.Search.REST.SearchServiceException</m:code>
<m:message xml:lang="en-US">Application does not have administrative permissions in tenant</m:message>
</m:error>
Using the following query in Postman
https://mytenant.sharepoint.com/_api/search/query?querytext='sharepoint'&refinementfilters='fileExtension:equals("docx")'
So my questions are :
Why are administrative permissions needed to do a search?
How can I create an application permission that is 'Full Control' + 'Also Search'?
Madness.
To make the app have administrative permissions in tenant, you need to change permission scope to tenant:
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
</AppPermissionRequests>

How do I hook into IDP initiated single logout to run custom code?

I'm using sustainsys.saml2.httpmodule. I would like to run some code to log the logout from an IDP initiated single log out. The user also does not seem to be logged out (IsAuthenticated is still true) after an IDP initiated single log out even though https://stubidp.sustainsys.com/Logout gives me a success result.
I can't seem to find anyone else needing the same functionality or having the same issues. My Sustainsys config is below.
<sustainsys.saml2 entityId="http://localhost:53758/Saml2"
returnUrl="http://localhost:53758/Common/Pages/Saml2Login.aspx"
authenticateRequestSigningBehavior="IfIdpWantAuthnRequestsSigned"
validateCertificates="false"
publicOrigin ="http://localhost:53758/">
<nameIdPolicy allowCreate="false" format="Unspecified"/>
<metadata cacheDuration="PT1440M" wantAssertionsSigned="true">
<organization name="ab" displayName="ab" url="https://www.example.com/" language="en" />
<contactPerson type="Technical" email="a#b.com" />
<requestedAttributes>
<add friendlyName ="Some Name" name="urn:someName" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" />
</requestedAttributes>
</metadata>
<identityProviders>
<add entityId="https://stubidp.sustainsys.com/Metadata"
signOnUrl="https://stubidp.sustainsys.com"
logoutUrl="https://stubidp.sustainsys.com/Logout"
allowUnsolicitedAuthnResponse="true"
binding="HttpRedirect"
wantAuthnRequestsSigned="true">
<signingCertificate storeName="CertificateAuthority" storeLocation="CurrentUser"
findValue="cdf7090a433561a843b51198b0ba6456" x509FindType="FindBySerialNumber" />
</add>
</identityProviders>
<serviceCertificates>
<add storeName="CertificateAuthority" storeLocation="CurrentUser" findValue="2cfe21cb930c19a341e9e30a07a3c123" x509FindType="FindBySerialNumber" />
</serviceCertificates>
</sustainsys.saml2>
You can use the LogoutCommandResultCreated notification. It will get called both when the redirect to the Idp is about to happen as well as after the response has been received from the Idp.

Create logon token using BI Platform RESTful SDK

I'm attempting to create a logon token using the BOE BI Platform RESTful SDK v4.1 (using RESTClient).
A GET request to http://server:6405/biprws/logon/long/ returns:
<attrs xmlns="http://www.sap.com/rws/bip">
<attr name="userName" type="string" />
<attr name="password" type="string" />
<attr name="auth" type="string" possibilities="secEnterprise,secLDAP,secWinAD,secSAPR3">secEnterprise</attr>
</attrs>
A POST to http://server:6405/biprws/logon/long/ with a single header of Content-Type: application/xml and a payload of
<attrs xmlns="http://www.sap.com/rws/bip">
<attr name="userName" type="string">myAccount</attr>
<attr name="password" type="string">myPassword</attr>
<attr name="auth" type="string" possibilities="secEnterprise,secLDAP,secWinAD,secSAPR3">secWinAD</attr>
</attrs>
returns:
<error>
<error_code>FWM 00006</error_code>
<message>Active Directory Authentication failed to log you on. Please contact your system administrator to make sure you are a member of a valid mapped group and try again. If you are not a member of the default domain, enter your user name as UserName#DNS_DomainName, and then try again. (FWM 00006)</message>
</error>
I've also tried attr name="userName" type="string">myAccount#mycompany.org</attr>, but with the same results.
A POST to http://server:6405/biprws/logon/adsso returns:
<error>
<error_code>RWS 00057</error_code>
<message>Method not allowed (RWS 00057)</message>
</error>
The credentials work with BI Launchpad and the CMC.
What am I missing?
First, a disclaimer -- I've only done REST WinAD with SSO, not manual logon. So I can't be absolutely sure that my suggestions below will fix your problem.
The call to /biprws/logon/adsso requires a GET not a POST, but that will likely not work until you have SSO working.
There are a few settings that are required for WACS to use WinAD, with or without SSO. The file is here:
SAP BusinessObjects\SAP BusinessObjects Enterprise XI 4.0\java\pjs\services\RestWebService\biprws\WEB-INF\web.xml
You will see a section commented out, starting with:
<!-- Kerberos filter section starts
Uncomment this section. Then set the following parameters:
idm.realm
idm.princ
idm.keytab
idm.kdc
idm.allowUnsecured
The values for these parameters should equal what was set in your system for BI launch pad. This is in:
SAP BusinessObjects\tomcat\webapps\BOE\WEB-INF\config\custom\global.properties
The format of the file is different (global.properties is a simple properties file, but web.xml is xml). So you can't just copy/paste the section, but you can copy the individual values. For example, in global.properties, you might see:
idm.keytab=C:/WINDOWS/bosso.keytab
This would be done in web.xml as:
<init-param>
<param-name>idm.keytab</param-name>
<param-value>C:/WINDOWS/bosso.keytab</param-value>
<description>
The file containing the keytab that Kerberos will use for
user-to-service authentication. If unspecified, SSO will default
to using an in-memory keytab with a password specified in the
com.wedgetail.idm.sso.password environment variable.
</description>
</init-param>
Couple of references:
http://myinsightbi.blogspot.com/
https://techwriter79.wikispaces.com/file/view/sbo41sp5_bip_rest_ws_en.pdf

Accept facebook login into my REST API

I have a backend server (Java / Spring / Spring Security).
Currently when users from mobile app login, they simply submit their username/password and Spring Security creates a Session and assign it to the request with a JSESSIONID.
We would now also have a button on the mobile app "Login with Facebook". Here is my understanding of how it will work.
mobile app uses facebook SDK to get an "access_token"
mobile app retrive USer Profile from facebook (name,surname,email etc..)
mobile checks (against MY server) if the username is unique
If username unique, call MY REST api, with something like this /login/facebook POST over SSL and passing the access_token, email, username etc...)
my server then checks if the access_token is valid
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
If yes, if the UID returned by facebook is already present in my local database, I signin the user as follow:
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(username, null, ROLE_USER));
If i don't find the UID, I just create a new user and login the user.
and from now on every request made to the server by the mobile will have the SESSION (created and attached by spring security) and the mobile app is authenticated
Could someone tell me if this is a good way of doing things ?
Should I stop using sessions and switch to Spring-Security-OAUTH2 ?
EDIT 1
Based on Dave advices here is the updated spring-security config:
<!- handle login by providing a token-->
<security:http pattern="/login/facebook" auto-config="false" use-expressions="true" entry-point-ref="loginUrlAuthenticationEntryPoint">
<security:custom-filter ref="facebookLoginFilter" position="FORM_LOGIN_FILTER"/>
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
<bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/login/facebook"></constructor-arg>
</bean>
<!-- handle basic username + password logins-->
<security:http auto-config="true" use-expressions="true" entry-point-ref="forbiddenEntryPoint">
<security:form-login login-processing-url="/security_check" authentication-failure-handler-ref="authFailureHandler"
default-target-url="/" always-use-default-target="true" authentication-success-handler-ref="authSuccessHandler" />
...
my others patterns..
...
</security:http>
<bean id="forbiddenEntryPoint"
class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />
<bean id="authSuccessHandler" class="my.package.AuthenticationSuccessHandlerImpl"/>
<bean id="authFailureHandler" class="my.package.AuthenticationFailureHandlerImpl"/>
<bean id="facebookLoginFilter" class="pl.jcommerce.ocean.web.ws.controller.FacebookLoginFilter">
<property name="requiresAuthenticationRequestMatcher" ref="loginRequestUrlHandler"></property>
<property name="authenticationManager" ref="authManager"></property>
</bean>
<security:authentication-manager id="authManager">
<security:authentication-provider ref="facebookAuthenticationProvider" />
</security:authentication-manager>
<security:authentication-manager>
<security:authentication-provider ref="webServiceUserAuthenticationProvider" />
</security:authentication-manager>
<bean id="loginRequestUrlHandler" class="org.springframework.security.web.util.matcher.RegexRequestMatcher">
<constructor-arg index="0" value="/login/facebook" />
<constructor-arg index="1" value="POST" />
<constructor-arg index="2" value="false" />
</bean>
Facebook is already using OAuth2 server side, and provides its own native SDK for clients, so I don't see any advantage in your case of using OAuth2 in your server as well, unless your use case extends beyond what you outline above. Spring OAuth2 also has client side support, but not in a native app, so I don't really see anything at all wrong in principle with your proposal. You didn't say in any detail where you would set the security context up in your server, and I think that might be an important detail -- it has to happen in the security filter chain in the right place to get the session to be updated.
I took a crack at implementing something like this based on Dave Syer's answer here along with the Spring Security Angular materials he put together. To see an example see my forked github repo, specifically the classes in the security package.

Installer cannot start the service under User

Installer installs windows service.
I provide form to user to select one of accounts to start the service:
Local service
Local System
Network System
User account
When I select User and enter Domain\Administrator account + pwd during the installation, service cannot be started.
When I select Local Service it started ok. After this I can change manually account to the same Domain\Administrator account and it started ok.
Why such different behavior?
How I can start service during the installation under any users' account?
Take a look at the Util Extensions User element. You can set the CreateUser attribute to no, the Name attribute to the [PROPERTY] that you are using in your UI for UserName Input and the LogonAsService attribute to yes. This will instruct WiX to grant your user the LogOnAsService right without having to write a custom action to call ntrights.exe.
Here's a sample as requested:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="*" Name="Setup" Language="1033" Version="1.0.0.0" Manufacturer="test" UpgradeCode="5c6b0f52-d024-4f1b-bfae-2dbb96b3ef15">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<UIRef Id="WixUI_Minimal" />
<Feature Id="ProductFeature" Title="Setup" Level="1">
<ComponentRef Id="serviceComponent" />
</Feature>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Setup">
<Component Id="serviceComponent" Guid="380bbddd-daa7-0744-517b-37da768f5570">
<File Id="serviceFile" Source="$(var.WindowsService.TargetPath)" KeyPath="yes" />
<ServiceInstall Id="serviceInstall" Name="WindowsService" DisplayName="WindowsService" Start="auto" Type="ownProcess" ErrorControl="ignore" Account=".\serviceaccount" Password="p2Ekutrekac34ph2" />
<ServiceControl Id="serviceControl" Name="WindowsService" Start="install" Stop="both" Remove="both" Wait="no" />
<util:User Id="user" CreateUser ="yes" Name ="serviceaccount" Password="p2Ekutrekac34ph2" LogonAsService="yes" UpdateIfExists="yes" RemoveOnUninstall="yes" PasswordNeverExpires="yes" FailIfExists="no" Domain="[ComputerName]" CanNotChangePassword="yes" Disabled="no" PasswordExpired="no"/>
</Component>
</Directory>
</Directory>
</Directory>
</Product>
</Wix>
You need to make sure that the selected user account has the right to log on as a service. It doesn't matter if you are an Administrator, you cannot install services for an user without giving him the log on right.
If setting this policy works, you also need to do it dynamically during install. A solution is to use ntrights.exe as a custom action. This custom action can use your custom properties which contains the user account information.
In case anyone wonders why the code from #Christopher Painter his answer didn't work. I had similar code that didn't work with error 1923.
Error 1923. Service '' () could not be installed. Verify that you have sufficient privileges to install system services.
MSI (s) (10:08) [15:55:00:161]: Product: '' (64 bit) -- Error 1923. Service '' () could not be installed. Verify that you have sufficient privileges to install system services.
Until I found that services.msc dialog needs to be closed during installation.
No idea why, but it works.
During development services.msc was open all the time, so I never noticed.