How to get Google refresh token with knpuniversity/oauth2-client-bundle? - symfony

I use knpuniversity/oauth2-client-bundle and league/oauth2-google to connect users in my Symfony 4 web app using a "Sign in with Google" feature. I followed this tuto. I registered my app in Google.
I set access_type: offline in the knpu.oauth2.client.google configuration (config/packages/knpu_oauth2_client.yaml file)
I try to get the user refresh token in my GoogleAuthenticator::getUser(League\OAuth2\Client\Token\AccessToken $credentials) method (which extends KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator).
Unfortunately, $credentials->getRefreshToken() always returns null.
Why don't I get the user refresh token ?

As per documentation, "Refresh tokens are only provided to applications which request offline access". So, when instantiating the provider you need to set the accessType.
use League\OAuth2\Client\Provider\Google;
$provider = new Google([
'clientId' => '{google-client-id}',
'clientSecret' => '{google-client-secret}',
'redirectUri' => 'https://example.com/callback-url',
'accessType' => 'offline',
]);
In knpu_oauth2_client configuration, you can do:
google:
type: google
client_id: '%env(OAUTH_GOOGLE_CLIENT_ID)%'
client_secret: '%env(OAUTH_GOOGLE_CLIENT_SECRET)%'
redirect_params: {}
access_type: offline

Related

Error: Firebase ID token has no "kid" claim

Firebase documentation states that custom claims can be accessed like so:
admin
.auth()
.verifyIdToken(idToken)
.then((claims) => {
if (claims.admin === true) {
// Allow access to requested admin resource.
}
});
I have implemented Firebase auth following this sample project and tutorial. Specifically, the token is being decoded inside Next.JS' getServerSideProps here.
It's also worth mentioning that I'm running this project in development mode with Firebase Emulators.
So, on calling:
verifyIdToken(token)
I get this error message:
Error: Firebase ID token has no "kid" claim. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
Any idea where I'm failing?
---------- UPDATE ----------
The decoded token has this info:
{
"header":{
"alg":"none",
"typ":"JWT"
},
"payload":{
"email":"user#test.test",
"email_verified":false,
"auth_time":1626004181,
"user_id":"MY_USER_ID",
"firebase":{
"identities":{
"email":[
"user#test.test"
]
},
"sign_in_provider":"password"
},
"iat":1626004181,
"exp":1626007781,
"aud":"MY_FIREBASE_PROJECT_ID",
"iss":"https://securetoken.google.com/MY_FIREBASE_PROJECT_ID",
"sub":"SOME_KEY"
}
}

Flutter + Firebase paswordless authentication not working

I am trying to implement passwordless authentication in my Flutter app using dynamic links, but the link the app receives always returns false for the isSignInWithEmailLink() method.
The dynamic link I created in the firebase console is the following:
Dynamic Links configuration
And the code I am using to send it is:
var acs = ActionCodeSettings(
url: "https://XXXXXX.page.link/eP3A",
handleCodeInApp: true,
iOSBundleId: "com.example.XXXXX",
androidPackageName: "com.example.XXXXXX",
androidInstallApp: true,
androidMinimumVersion: "12",
);
FirebaseAuth.instance.sendSignInLinkToEmail(
email: email, actionCodeSettings: acs)
.catchError((onError) =>
print('Error sending email verification $onError'))
.then((value) => print('Successfully sent email verification'));
I am able to receive the link in the email, and when clicking on it, it opens the app. However, the link always return false in isSignInWithEmailLink(). Am I missing something? I've tried different combinations on the ActionCodeSettings object but none of them seem to work. I am using the latest versions of firebase_auth and firebase_dynamic_links.

JWT key for mercure

I try generate JWT key for Mercure settings
I use this manual
https://medium.com/#stefan.poeltl/instant-realtime-notifications-with-symfony-and-mercure-e45270f7c8a5
for pass myJWTKey JWT is
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6W10sInB1Ymxpc2giOlsiKiJdfX0.iTVjHoLv9bB-O5RNnTtzOFxIW-YECk2JXZeMekZ4GwA
I found a token builder ( Signed JSON Web Token )
http://jwtbuilder.jamiekurtz.com/
but I find no setting that generates a correct JWT. How do I do it? What I miss?
I tried generate token for env settings
MERCURE_PUBLISH_URL=http://mercure.dev:3000/.well-known/mercure
# The default token is signed with the secret key: !ChangeMe!
MERCURE_JWT_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6W10sInB1Ymxpc2giOlsiKiJdfX0.iTVjHoLv9bB-O5RNnTtzOFxIW-YECk2JXZeMekZ4GwA
###< symfony/mercure-bundle ###
This token is for default password in docker-compose
mercure:
image: dunglas/mercure
environment:
# You should definitely change all these values in production
- JWT_KEY=myJWTKey
- DEMO=1
- ALLOW_ANONYMOUS=1
- HEARTBEAT_INTERVAL=30s
- ADDR=:3000
if I change myJWTKey to mysecure pass - how I can generate token?
Just an addition to a great answer by #Daidon. Mercure bundle uses lcobucci/jwt and registers it's factory as a service.
If you want to generate JWT do the following
Pass the factory as an argument with #mercure.hub.default.jwt.factory (here default is for your hub name)
In your service/controller
public function generateJwt(LcobucciFactory $factory): string
{
return $factory->create(['*']);
}
UPD: even easier way to get a JWT token
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mercure\Authorization;
public function generateJwt(Request $request, Authorization $authorization): string
{
return $authorization->createCookie($request, ['*'])->getValue();
}
You can use different libraries for doing that, a very simple and fast one would be php-jwt
Then do
composer require firebase/php-jwt
And in the code you can do then:
use \Firebase\JWT\JWT;
$key = "12345678";
$payload = [
'mercure' => [
'publish' => ['*'],
],
];
$jwt = JWT::encode($payload, $key); // holds valid jwt now
The library will automatically inject the headers that you need (default: alg HS256, typ: jwt) and set the payload for you. Then it encodes it to base64 and signs it also.
Go on and set a cookie with this jwt or use it in authorization header now :-)
If you want to use the JWT for subscriber authentication, don't forget to put the subscribe key in the payload.
$payload = [
'mercure' => [
'subscribe' => ['*'], // make this a list of concrete topics, don't use *
],
];
Also for that usecase, you can carry around some data in the cookie, by providing a payload key with an object:
$payload = [
'mercure' => [
'subscribe' => ['*'],
'payload' => [
'userId' => $user->getId()
]
],
];
Apologize for late answer, you can simply generate new jwt token once using the official page https://jwt.io/.

How to properly configure Amplify Analytics?

I need some help understanding how to configure AWS Pinpoint analytics in Amplify. I'm currently using Amplify for Auth and have it configured like this in my index.js file:
export const configureAmplify = () => {
window.LOG_LEVEL="DEBUG"
Hub.listen('auth', data => handleAmplifyHubEvent(data))
Hub.listen('analytics', data => handleAmplifyHubEvent(data))
Amplify.configure({
Auth: {
identityPoolId: "redacted",
region: "us-west-2",
userPoolId: "redacted",
userPoolWebClientId: "redacted",
mandatorySignIn: false,
cookieStorage: {
domain: process.env.NODE_ENV === 'development' ? "localhost" : "redacted",
path: '/',
expires: 1,
secure: false
}
}
})
}
To add Analytics, I started by adding this to my configureAmplify() function:
Analytics: {
disabled: false,
autoSessionRecord: true,
AWSPinpoint: {
appId: 'redacted',
region: 'us-east-1',
endpointId: `wgc-default`,
bufferSize: 1000,
flushInterval: 5000, // 5s
flushSize: 100,
resendLimit: 5
}
}
Upon user sign-in or refresh from cookie storage I called
Analytics.updateEndpoint({
address: user.attributes.email, // The unique identifier for the recipient. For example, an address could be a device token, email address, or mobile phone number.
attributes: {
},
channelType: 'EMAIL', // The channel type. Valid values: APNS, GCM
optOut: 'ALL',
userId: user.attributes.sub,
userAttributes: {
}
})
After doing this, it seems to me that the data in the Pinpoint console is not accurate. For example, there are currently 44 sessions displayed when no endpoint filter is applied. If I add an endpoint filter by userAttributes: userId then no matter which ID I select, it shows all 44 sessions associated with that user. I suspect that is because the EndpointID is established at startup, and is not changed by the updateEndpoint call.
I have also tried omitting the Analytics key when I initially configure Amplify, and then calling Analytics.configure() after the user is signed in. With this approach, I can construct a user-specific endpointId. However, I think that doing it this way will mean that I don't capture any of the Authentication events (sign-in, sign-up, auth failure), because Analytics is not configured until after they occur.
So my question is what is the proper timing for configuring Amplify Analytics? How can I accurately capture session, auth and custom events, AND uniquely identify them by user id?
It's not necessary to assign a custom endpoint id, amplify will handle it automatically and all events will be tracked per device. Instead, if you really need it, update the endpoint with the userId after sign-in.
The advantage of adding the userId is that all the endpointIds of a user are automatically associated to that userId, thus when you update a user's attribute, it will be synchronized across the endpoints.
As you are using Cognito, Amazon Cognito can add user IDs and attributes to your endpoints automatically. For the endpoint user ID value, Amazon Cognito assigns the sub value that's assigned to the user in the user pool. To learn about adding users with Amazon Cognito, see Using Amazon Pinpoint Analytics with Amazon Cognito User Pools in the Amazon Cognito Developer Guide.

Should firebase auth onCreate trigger have more data?

I'm using functions.auth.user().onCreate() as part of a firestore project, and trying to set up some default data when a new user registers. For the front end, I'm using firebase-ui, with Google and Email/Password providers enabled.
When I sign in with an email and password, the UI widget prompts to enter a name and set a password. I was expecting to see the name as part of the user parameter in the onCreate() function call, but I'm getting practically nothing:
user: { email: 'xxx#yyyy.co.uk',
emailVerified: false,
displayName: null,
photoURL: null,
phoneNumber: null,
disabled: false,
providerData: [],
customClaims: {},
passwordSalt: null,
passwordHash: null,
tokensValidAfterTime: null,
metadata:
UserRecordMetadata {
creationTime: '2018-11-20T15:06:01Z',
lastSignInTime: '2018-11-20T15:06:01Z' },
uid: 'QDJ5OJTwbvNo2QNDVQV9VsxC2pz2',
toJSON: [Function] }
Not even getting the provider info so I can tell which 'kind' of user registered. It's almost like this function is triggered before the user record has been populated (except the email address does get through). Also, registrations via the Google provider come with a fully-populated user record, so I guess this is a problem with Email/Password specifically.
Is this a bug, or am I missing something? I didn't see anything else useful in the context parameter either.
The fact that displayName is not populated in the Cloud Functions onCreate trigger for email+password is expected. The function is triggered from the first API call (createUserWithEmailAndPassword()), while the display name is set with a second API call (updateProfile).
The usual workaround would be to create a Cloud Function to update the user profile, as shown here: Firebase Auth+Functions | create user with displayName
I also highly recommend filing a feature request to be able to have a Cloud Function triggered on profile changes.

Resources