how to use revoke all valid access/refresh tokens of a particular user in openiddict (2.0.1) - access-token

Is there any way to trigger revoke all valid token of a user in openiddict version 2.0.1,
I added following code in my controller (took reference from Openiddict github issue #642 ,#380,#381)-
//code to use OPenIddictTokenManager class with dependency injection
OpenIddictTokenManager tokenManager
//logic to logout all active tokens//
foreach (var token in await _tokenManager.FindBySubjectAsync(user.Id))
{
if (await _tokenManager.IsValidAsync(token))
{
await _tokenManager.RevokeAsync(token);
}
}
And starup.cs file server options-
.AddServer(options =>
{
options.UseMvc();
options.EnableTokenEndpoint("/***************");
options
.AllowPasswordFlow()
.AllowRefreshTokenFlow()
.AllowCustomFlow("id_token**")
.AllowCustomFlow("impers***")
.AllowCustomFlow("anony**")
.AllowCustomFlow("sms_t***");
options.AcceptAnonymousClients();
if (HostingEnvironment.IsDevelopment())
{
options.DisableHttpsRequirement();
}
})
.AddValidation();
//I also tried with server and validation options.UseReferenceTokens(), but getting error: 401 "invalid_grant". Sometime 500 internal server error.
Can you please give any fix?

Related

Log-in users in flutter through social accounts with laravel-socialite backend

I am working on a flutter application, and I want to implement social login (Google and Facebook).
My API is implemented with Laravel and uses Laravel-socialite to authenticate users, there is the backend, web frontend (using VueJs) and now I am working on the mobile application using flutter.
The web application is working good (using the vue-social-auth package).
What I have done till now:
Used flutter_google_sign_in to handle authentication on the flutter app.
Did configure the package and I can successfully get user info through that package.
Problem I am facing:
What I don't seem to get working is to send the user that just logged in to the backend in order to provide an in-app user experience.
This is what the vue-social-auth package provides and what I send to the backend, which is working fine:
{code: "4/0AY0e-g442SMxdtLb_MVdQ63u1ydp48bbCRQco5Azoyf3y1rvYybDabyZGOvwAs7ZFJDQHA", scope: "email+profile+openid+https://www.googleapis.com/au…le+https://www.googleapis.com/auth/userinfo.email", authuser: "0", prompt: "consent"}
And this is what flutter_google_sign_in gives (aside of the user profile data:
idToken: "",
accessToken: "",
serverAuthCode: "",
serverAuthCode is always null.
How can I make it so that, using the same API logic, log-in users on flutter through social accounts?
Thank you.
Apparently, google sign in doesn't work on flutter except with Firebase/some cloud API backend service. I was using a local Laravel API for user auth so adding google sign in functionality requires setting up a firebase account/profile, downloading and adding the googleservices.json file to flutter project as explained in google_sign_in package installation manual. You also need to import firebase-auth package
Flutter Code (I use flutter modular pattern but same applies with Bloc/Provider if you get the idea as explained by Hamza Mogni above)
import 'package:google_sign_in/google_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<LoginResponseModel> googleLoginResponse() async {
String url = env['API_BASE_URL'] + '/api/auth/google';
//click on google sign in. Get accessToken from google through googlesignin
plugin.
//Send accessToken to socialite in backend to request/create user data
GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn();
if (googleSignInAccount == null) {
print('Google Signin ERROR! googleAccount: null!');
return null;
}
GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
//this is user access token from google that is retrieved with the plugin
print("User Access Token: ${googleSignInAuthentication.accessToken}");
String accessToken = googleSignInAuthentication.accessToken;
//make http request to the laravel backend
final response =
await http.post(
url,
body: json.encode({"token": accessToken}),
headers: {"Content-Type": "application/json"});
if (response.statusCode == 200 || response.statusCode == 422) {
return LoginResponseModel.fromJson(
json.decode(response.body), // {'message':'Google signin successful'}
);
} else {
throw Exception('Failed to load data!');
}
}
For Logout function, you need to signout of both firebase and google account instance or you will always be logged in by the first known/used google account in subsequent login attempts.
Future<LogoutResponseModel> logout() async {
try {
await _auth.signOut();
await _googleSignIn.disconnect();
} catch (e) {
print('Failed to sign out ' + e.toString());
}
//api route to destroy sanctum token. santum token is added as authorization header
var url = env['API_BASE_URL'] + "/api/logout";
final response =
await http.post(Uri.tryParse(url), headers: {'Bearer ' $sanctumtoken});
if (response.statusCode == 200 || response.statusCode == 422) {
return LogoutResponseModel.fromJson(
json.decode(response.body),
);
} else {
throw Exception('Failed to load data!');
}
}
Laravel Code (route to controller method is api/auth/google, method expects to receive google access token from flutter app)
public function requestTokenGoogle(Request $request) {
// Getting the user from socialite using token from google
$user = Socialite::driver('google')->stateless()->userFromToken($request->token);
// Getting or creating user from db
$userFromDb = User::firstOrCreate(
['email' => $user->getEmail()],
[
'email_verified_at' => now(),
'first_name' => $user->offsetGet('given_name'),
'last_name' => $user->offsetGet('family_name'),
'avatar' => $user->getAvatar(),
]
);
// Returning response
$token = $userFromDb->createToken('Laravel Sanctum Client')->plainTextToken;
$response = ['token' => $token, 'message' => 'Google Login/Signup Successful'];
return response($response, 200);
}
I have solved it, after some digging I found out Laravel-Socialite has the functionality to log in users using their token built-in:
Quoting Socialite documentation:
If you already have a valid access token for a user, you can retrieve their details using Socialite's userFromToken method.

Azure/Msal authentication inside PowerApp Component Framework returns AADSTS50177 error

I created a simple PowerApps Component Framework using the pac pcf init command.
After successfully packaging and importing this skeleton PCF application to my demo tenant I tried to add MSAL authentication to it.
I used the #azure/msal npm package to write a typescript configuration and login without adding React or Angular npm packages. I only used #azure/msal and package added during the pcf create process.
The final goal was to use the token received from the msal authentication make a request on a authorized method in my Wep Api.
The problem is that my Web Api is not located in my demo tenant and the user that is used for msal authentication is from the demo tenant and does not exist on the tenant of my Web Api.
I cannot change the login user in the popup window as it only displays the error message, and the guest user that was added to the demo tenant, that has access to the Web API cannot have Certificates added to it through portal azure or portal office admin center pages.
This is my login configuration(I will omit the tenant names and client id for the work tenant):
import { AuthenticationParameters, Configuration, UserAgentApplication } from '#azure/msal';
import { AuthOptions, CacheOptions, FrameworkOptions } from "#azure/msal/lib-commonjs/Configuration";
public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container:HTMLDivElement)
{
// Add control initialization code
const auth: AuthOptions = {
clientId:'clientid',
authority:'https://login.microsoftonline.com/tenantid',
redirectUri:'redirect uri',
validateAuthority: true
};
const cache: CacheOptions = {
cacheLocation:"localStorage"
};
const framework: FrameworkOptions = {
protectedResourceMap: new Map([
['web api url',['https://tenantid/clientid/uniquename (scope)']],
['CRM work sandbox',['CRM work sandbox user impersonation permission(scope)']]
]),
unprotectedResources:[]
};
const config: Configuration = {
auth: auth,
cache: cache,
framework: framework
};
const params: AuthenticationParameters = {
authority: 'https://login.microsoftonline.com/tenantid',
scopes:['offline_access',
'https://tenantid/clientid/uniquename(scope)',
'CRM work sandbox user impersonation permission(scope)'],
redirectUri:'web api redirect uri'
};
const userAgentApplication = new UserAgentApplication(config);
const login = userAgentApplication.loginPopup(params).then(data => {
console.log(data);
let user = userAgentApplication.getAccount();
console.log(user);
if (user) {
// signin successful
console.log('success');
} else {
// signin failure
console.log('fail');
}
}, function (error: string) {
// handle error
console.log('Error' + error);
});
}
The error message displayed:
AADSTS50177: User account 'user name' from identity provider
'https://sts.windows.net/guid/' does not exist in tenant 'name'
and cannot access the application 'client id'(name of registered
app in portal azure) in that tenant. The account needs to be
added as an external user in the tenant first. Sign out and
sign in again with a different Azure Active Directory user account.
Is there a way to test this without adding the pcf or account in my work tenant ?

unable to obtain access token silently using msal.js if user is logged in from msal .net code

I downloaded the example from GitHub to experiment with Azure AD B2C https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi
I have reconfigured this Web App for my AADB2C and all works well.
To also experiment with MSAL.js, I added a new page that implements the get access token for logged in user. User is logged in using server side code.
However, I was not able to get cached user session for the logged user as it seems MSAL.js dose not know the user already logged in from the server side code and vise-versa.
here is the code I used to get the logged user session and try to get token silently.
if (msalInstance.getAccount()) {
var tokenRequest = {
scopes: ["user.read", "mail.send"]
};
msalInstance.acquireTokenSilent(tokenRequest)
.then(response => {
// get access token from response
// response.accessToken
})
.catch(err => {
// could also check if err instance of InteractionRequiredAuthError if you can import the class.
if (err.name === "InteractionRequiredAuthError") {
return msalInstance.acquireTokenPopup(tokenRequest)
.then(response => {
// get access token from response
// response.accessToken
})
.catch(err => {
// handle error
});
}
});
} else {
// user is not logged in, you will need to log them in to acquire a token
}
the msalInstance.getAccount() function will always return null even the user has already logged in using the Microsoft.Identity Code (MSAL C# lib).
If anyone could if it is possible to get access token silently if the user is logged in using service side code.
Had the same issue.
Strangely the developers Msal.js have security concerns when your .js-application
adopts the session started in your .net-application. Although client side is not the right place to put that kind of security mechanism.
Here is where i found that info: related issue on github
You can adopt the .net-started session in your .js-application with msal.js by adding session id or login_hint to acquireTokenSilent(). More info can be found here: official msal.js documentation

Invalid signature while validating Azure ad access token, but id token works

I am getting invalid signature while using jwt.io to validate my azure ad access token. My id token, however, validates just fine!
I have seen and tried the solutions suggested in
Invalid signature while validating Azure ad access token
and
https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx
but neither works for my access token.
The access and Id token is generated via Adal.js:
var endpoints = {
"https://graph.windows.net": "https://graph.windows.net"
};
var configOptions = {
tenant: "<ad>.onmicrosoft.com", // Optional by default, it sends common
clientId: "<app ID from azure portal>",
postLogoutRedirectUri: window.location.origin,
endpoints: endpoints,
}
window.authContext = new AuthenticationContext(configOptions);
Why can I validate my ID token, but not my access token?
Please refer to thread : https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/609
but if look at the Jwt.Header you will see a 'nonce'. This means you need special processing. Normal processing will fail.
So if nonce includes in access token , validate signature with JWT.io or JwtSecurityToken won't success .
If anyone else has invalid signature errors, you should check this comment : https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/521#issuecomment-577400515
Solved the issue for my configuration.
Essentially, if you are getting access tokens to access your own resource server and not the Graph API, your scopes parameter should be [CLIENT_ID]/.default (and if you are using the access token to access the Graph API, you don't need to validate the token yourself)
Thanks to Nan Yu I managed to get token that can be validated by any public jwt validator like jwt.io
(couldn't put my comment in the comments section under Nan Yu's answer because its too long).
So as I understand the point from the discussion mentioned by Nan Yu that by default Azure AD generates tokens for Microsoft Graph and these tokens use special signing mechanism so that it is not possible to validate signature using public validators (except jwt.ms Microsoft's validator which most probably knows what mysterious special handling means :) ).
To get access token not for Microsoft Graph that can be validated using public validators I had to:
Remove any Microsoft Graph related scopes (by default I had only one scope configured User.Read so removed it in appConfig > API permissions)
create a custom scope for your application (appConfig > Expose an API > Add scope ...) this scope will look like api://{application-id}/scope-name
add just created scope in the application API permissions (appConfig > API permissions > Add api permission > My APIs > select your application > Delegated Permissions > Check your scope > Add permission)
then use this scope in your openid client scopes, in my case I have: openid offline_access {application-id}/scope-name
Note that in the openid client config newly created scope is used without api:// prefix (offline_access I have to enable refresh_token can be ignored if refresh token mechanism is not used)
Well thanks to #Antoine I fix my code. Here I will let my personal vue.js plugin that is working for everybody else reference:
import { PublicClientApplication } from '#azure/msal-browser'
import { Notify } from 'quasar'
export class MsalService {
_msal = null
_store = null
_loginRequest = null
constructor (appConfig, store) {
this._store = store
this._msal = new PublicClientApplication(
{
auth: {
clientId: appConfig.auth.clientId,
authority: appConfig.auth.authority
},
cache: {
cacheLocation: 'localStorage'
}
})
this._loginRequest = {
scopes: [`${appConfig.auth.clientId}/.default`]
}
}
async handleResponse (response) {
await this._store.dispatch('auth/setResponse', response)
const accounts = this._msal.getAllAccounts()
await this._store.dispatch('auth/setAccounts', accounts)
if (accounts.length > 0) {
this._msal.setActiveAccount(accounts[0])
this._msal.acquireTokenSilent(this._loginRequest).then(async (accessTokenResponse) => {
// Acquire token silent success
// Call API with token
// let accessToken = accessTokenResponse.accessToken;
await this._store.dispatch('auth/setResponse', accessTokenResponse)
}).catch((error) => {
Notify.create({
message: JSON.stringify(error),
color: 'red'
})
// Acquire token silent failure, and send an interactive request
if (error.errorMessage.indexOf('interaction_required') !== -1) {
this._msal.acquireTokenPopup(this._loginRequest).then(async (accessTokenResponse) => {
// Acquire token interactive success
await this._store.dispatch('auth/setResponse', accessTokenResponse)
}).catch((error) => {
// Acquire token interactive failure
Notify.create({
message: JSON.stringify(error),
color: 'red'
})
})
}
})
}
}
async login () {
// this._msal.handleRedirectPromise().then((res) => this.handleResponse(res))
// await this._msal.loginRedirect(this._loginRequest)
await this._msal.loginPopup(this._loginRequest).then((resp) => this.handleResponse(resp))
}
async logout () {
await this._store.dispatch('auth/setAccounts', [])
await this._msal.logout()
}
}
// "async" is optional;
// more info on params: https://quasar.dev/quasar-cli/boot-files
export default ({
app,
store,
Vue
}) => {
const msalInstance = new MsalService(
app.appConfig, store
)
Vue.prototype.$msal = msalInstance
app.msal = msalInstance
}
PD: using quasar framework
If you are using msal.js library with react, add this to your auth configuration.
scopes: [`${clientId}/.default`]
Editing scopes fixed issue for me

Removing cached Google access token that has been revoked

In my c# desktop app, I'm using Google's API to authenticate and retrieve the access token for an API. I noticed that the API will cache this token and use it again until it expires.
Using my browser, I was able to revoke the token using:
https://accounts.google.com/o/oauth2/revoke?token=1/xxxxxxx
I did this to test out how the API handles revoked tokens. As exepected, the API fails. The problem I have though is getting the API to use a new token. It continues to retrieve the cached token, even though it's been revoked. The following code is used to authenticate the use of the API:
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, Scopes, "user", CancellationToken.None);
How can I get the API to remove the cached token and request a new one?
You have to log out before getting a new token. You can read about OAuth 2.0 here https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth?hl=uk .
Below how I've done that for my windows phone 8 application:
var string const GoogleTokenFileName = "Google.Apis.Auth.OAuth2.Responses.TokenResponse-user"
public async Task LoginAsync()
{
await Logout();
_credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets
{
ClientId = _xmlfile.GoogleClientId,
ClientSecret = _xmlfile.GoogleClientSecret
}, new[] { Oauth2Service.Scope.UserinfoProfile }, "user", CancellationToken.None);
return session;
}
public async Task Logout()
{
await new WebBrowser().ClearCookiesAsync();
if (_storageService.FileExists(GoogleTokenFileName))
{
_storageService.DeleteFile(GoogleTokenFileName);
}
}
Hope it helps.

Resources