How to authenticate firebase cloud functions in Functions Emulator using the users stored in Firebase Authentication emulator? - firebase-authentication

My firebase cloud function contains protected routes that can only be accessed when passed a valid IdToken in the request header. cloud functions API looks like this
functions/index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "DB_URL"
});
const express = require('express');
const app = express();
const authenticate = async (req, res, next) => {
if (!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) {
res.status(403).send('Unauthorized');
return;
}
const idToken = req.headers.authorization.split('Bearer ')[1];
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
req.user = decodedIdToken;
next();
return;
} catch(e) {
res.status(403).send('Unauthorized');
return;
}
};
app.use(authenticate);
app.get('/protected', async (req, res) => {
return res.send('OK');
});
exports.api = functions.https.onRequest(app);
Initially, I was using the Firebase Authentication to create new user and to get IdToken
Creating new user
curl 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]' \
-H 'Content-Type: application/json' \
--data-binary '{"email":"[user#example.com]","password":"[PASSWORD]","returnSecureToken":true}'
Getting IdToken so I can pass it to the firebase cloud functions to access the protected routes
curl 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[API_KEY]' \
-H 'Content-Type: application/json' \
--data-binary '{"email":"[user#example.com]","password":"[PASSWORD]","returnSecureToken":true}'
calling my protected cloud functions using this approach is working fine
curl --location --request GET 'http://localhost:5001/abc-production/us-central1/api/protected/' \
--header 'Authorization: Bearer SECRET_ID_TOKEN'
Now, Instead of using the Firebase Authentication, I'd like to use the Authentication Emulator to create new users and generate IdToken's.
I can create new users using the Auth emulator UI, but how do I generate the access token of those users? Are there any API endpoints that can return the IdToken of the locally saved user so that I can test my protected API without adding users in production?
Also, When I run the auth emulator, IdToken's generated using the production Firebase Authentication environment doesn't work.
"code": "auth/argument-error",
"message": "Firebase ID token has invalid signature. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token."

I just answered a very similar question here. The answer is the same so sorry if I'm duplicating...
Basically, to authenticate a user you can make the following HTTP POST request (assuming your Authentication emulator runs on port 9099):
http://localhost:9099/identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=any_key_you_want
Body (JSON):
{
"email": "your-user#mail.com",
"password": "some-password"
}

The emulator is intended only for local testing and not for production and you cannot use the IdToken issued by the emulator to access other services like Firebase Functions because and as the documentation says:
For security reasons, the Authentication emulator issues unsigned ID
Tokens which are only accepted verifyIdToken and createSessionCookie
of the Firebase Admin SDK when running inside the Cloud Functions
emulator.
So, these token are only valid in the emulator environment and if you want to use them with Firebase Function, you can do that only with the Functions emulator. check this guide on how to run the functions locally.

Related

Aws-amplify integration with React Native Token - unauthorized

I have configured my React Native application to use a Cognito user pool that is used for user identity management and authentication using AWS Amplify. I am using Custom Authentication for user management. I am able to register, log in and perform other user management-related tasks. I also have an API gateway and a few Lambda Functions which I want to access through my React Native app. When I sign in, I receive a JWT Token which I want to send to the API gateway to access my Lambdas, but no matter what I do I get an 'unauthorized' 403 or 401 message from my API Gateway.
My question is: How can I expose the API gateway/ Lambdas to the Cognito user pool users and Why the token generated by Cognito itself is unauthorized to access my api gateway.
P.S. - I used postman with the right Auth URL and settings, the postman token itself is authorized to access the API gateway and lambdas. (The user credentials are the same as I use with the React Native app)
I have spent a few days, any pointers in the right direction would be very helpful.
Thanks in advance.
NPN
Amplify Config:
const awsmobile = {
aws_project_region: 'us-XXXX-X',
aws_cognito_region: 'us-XXXX-X',
aws_user_pools_id: 'us-XXXX-XXXXXX',
aws_user_pools_web_client_id: 'XXXXh1i5nXXXX',
//aws_user_pools_web_client_secret: 'XXXXXoofuu0lXXXX',
oauth: {
domain: 'XXXXXXXX.us-XXXX-X.amazoncognito.com',
scope: ["email", "openid", "aws.cognito.signin.user.admin"]
},
aws_cognito_username_attributes: ['EMAIL'],
aws_cognito_social_providers: ['GOOGLE'],
aws_cognito_signup_attributes: ['XXXXX', 'XXXXX', 'EMAIL', 'XXXXXX'],
aws_cognito_mfa_configuration: 'OFF',
aws_cognito_mfa_types: [],
aws_cognito_password_protection_settings: {
passwordPolicyMinLength: 8,
passwordPolicyCharacters: ['REQUIRES_LOWERCASE', 'REQUIRES_UPPERCASE', 'REQUIRES_NUMBERS', 'REQUIRES_SYMBOLS'],
},
aws_cognito_verification_mechanisms: ['EMAIL'],
};
export default awsmobile;
import { Auth } from 'aws-amplify';
const login = async (username: string, password: string) => {
const response = await Auth.signIn(username, password);
console.log(response.data.signInUserSession.accessToken.jwtToken);
return response;
};

Cognito Auth.sendCustomChallengeAnswer gives "AuthClass - Failed to get the signed in user No current user"

I am trying to make API (Lambda and API gateway) for sign in and verify auth using OTP for password-less authentication. The target is to make front end using angular and mobile application using Flutter but there is no support of AWS Amplify for flutter. So going through to create those API to serve my purpose. The frontend code(Auth.signIn and Auth.sendCustomChallengeAnswer) works great but using same code the verify auth API is not working. Sharing my code.
Sign In API:
await Auth.signIn(phone);
Verify Auth API: (Returned c['Session'] from DynamoDB which is stored in during signIn)
let otp = body['otp'];
const poolData = {
UserPoolId: '------ pool id -------',
ClientId: '------ client id -------'
};
const userPool = new CognitoUserPool(poolData);
const userData = {
Username: '+12014222656',
Pool: userPool
};
this.cognitoUser1 = new CognitoUser(userData);
this.cognitoUser1['Session'] = c['Session'];
await Auth.sendCustomChallengeAnswer(this.cognitoUser1, otp);
const tokenDetails = await Auth.currentSession()
response = {
'statusCode': 201,
'body': JSON.stringify({
message: 'Verification successful',
body:tokenDetails
})
}
After debugging frontend Auth.signIn response and Lambda API Auth.signIn response i investigated that an extra "storage" object returned when signing in from frontend and appended on this.cognitoUser1 before sending through Auth.sendCustomChallengeAnswer . See the attached screenshot below:
Is this the reason for successful verifying OTP from frontend? If so, what about making API (using Lambda and API gateway) and where it stores this storage object. Stuck here. Any suggestion and help will be appreciated.
You can set your own storage: https://docs.amplify.aws/lib/auth/manageusers/q/platform/js#managing-security-tokens

Identity Server 4

Beginner level query alert. IdentityServer4 Tutorial After going through the tutorials what I inferred was that-
I create an authorization server, whose job is to issue token for the client with proper authentication.
My Authorization Server runs first, and includes information and definitions of the API and client.
The API has an authentication middleware that validates the incoming token to make sure if its coming from a trusted source and also its scope.
The client requests a token from the authorization server and then sends request to the API with the token received.
For all this, I had to run the authorization server first, the API next and then the Client. My requirement is that I don't need a start and stop server which runs separately to take care of authentication. I have one API and I need it to double as the authorization server too. Is this possible? Is it possible for the API to generate tokens, validate them and then tend to the requests, all the while using IdentityServer4.
Update Jan 2020: For a ASP.NET Core 3.1 example of using IdentityServer4 in the same project as ASP.NET Core API controllers, you can have a look at my IdentityServer4 with MVC Controllers and AppInsights sample repo. It's goal was to test AppInsights, but it does demonstrate a SPA stub that calls both OpenID endpoints (⚠ in a non-recommended wa, using client credentials), and controller endpoints.
Although typically the Auth Server will be separate from the Resource Server, this doesn't need to be the case. You can just add all of it to one application. Here's an example.
Create a new ASP.NET Core (I used 2.0) Web API application.
Install-Package IdentityServer4 -Version 2.0.0-rc1 (at the time of writing rc1 is the version with .NET Core 2.x support)
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Set [Authorize] on ValuesController from the template
Add this code to Configure(...) in class Startup above app.UseMvc():
// calls app.UseAuthentication() for us
// See: http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html
app.UseIdentityServer();
Add this code to ConfigureServices(...) in class Startup:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(new[]
{
new ApiResource
{
Name = "MyApi",
ApiSecrets = { new Secret("supersecret".Sha256()) },
Scopes = { new Scope("myapi") },
}
})
.AddInMemoryClients(new[]
{
new Client
{
ClientId = "api",
ClientSecrets = { new Secret("supersecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = { "myapi" },
}
})
.AddTestUsers(new List<TestUser>
{
new TestUser
{
SubjectId = "some-unique-id-12345678980",
Username = "john",
Password = "123456"
}
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opts =>
{
opts.Authority = "http://localhost:51689";
opts.Audience = "MyApi";
opts.RequireHttpsMetadata = !env.IsDevelopment();
});
If you now F5 the app it will show an empty page because of a "401 Unauthorized" response. You can also now check this endpoint: http://localhost:51689/.well-known/openid-configuration (with your dev port of course).
You can also do this now:
curl -X POST \
http://localhost:51689/connect/token \
-H 'authorization: Basic YXBpY2xpZW50aWQ6c3VwZXJzZWNyZXQ=' \
-H 'cache-control: no-cache' \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=john&password=123456&grant_type=password'
Note that the authorization header contains a base64 encoded string representing the string "apiclientid:supersecret". This should give you a result like this:
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjczODhkMjY0MDg4Y2NjOGRiZTcwODIzZGIxYzY3ZWNkIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDUwODE3OTAsImV4cCI6MTUwNTA4NTM5MCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MTY4OSIsImF1ZCI6WyJodHRwOi8vbG9jYWxob3N0OjUxNjg5L3Jlc291cmNlcyIsIk15QXBpIl0sImNsaWVudF9pZCI6ImFwaWNsaWVudGlkIiwic3ViIjoic29tZS11bmlxdWUtaWQtMTIzNDU2Nzg5ODAiLCJhdXRoX3RpbWUiOjE1MDUwODE3OTAsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsibXlhcGkiXSwiYW1yIjpbInB3ZCJdfQ.sxWodlJKDJgjoOj-8njZ8kONOqiKgj3E5YlKXGX5cz-WqUK7RHKJacNX09D00Y8YtmZpkc5OrY0xzOx7UuSAtDku4oOX_1o38XEGJPBSJHdjqgVGSOU-hwDkzin8HSRJ0Kna1vM3ZzTh80cFTVhP8h903GAPRrAyV8PtRXnwV0CPel8NdvML6dV-mfDpGi0l7crp-TPnH4nIG0olpRYUPV5EsgCVMG9vswnOnKz3RPOGaU8yJy7_9mbQW5GHKfN0J6swiSt5rY3NKs_t1P9-tnCDKBOAafaXjLEO3Kx4fP4xTgwK92uKcEDDnRZo_-T0CkBxnSQm0oz1sUyrW8_3Pg",
"expires_in": 3600,
"token_type": "Bearer"
}
In addition to the option of switching to other authentication flows, you can also add a controller method like this:
[Route("api/token")]
public class TokenController
{
[HttpPost("request")]
public async Task<JObject> Request(string username, string password)
{
var tokenClient = new TokenClient("http://localhost:51689/connect/token", "apiclientid", "supersecret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(username, password);
if (tokenResponse.IsError) { /* Log failed login attempt! */ }
return tokenResponse.Json;
}
}
And then call it like this:
curl -X POST \
http://localhost:51689/api/token/request \
-H 'cache-control: no-cache' \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=john&password=123456'
This should give a similar response as above.
You can now provide this access_token insde a header Authorization: Bearer access_token_should_go_here like this:
curl -X GET \
http://localhost:51689/api/values \
-H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjczODhkMjY0MDg4Y2NjOGRiZTcwODIzZGIxYzY3ZWNkIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDUwODIyODQsImV4cCI6MTUwNTA4NTg4NCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MTY4OSIsImF1ZCI6WyJodHRwOi8vbG9jYWxob3N0OjUxNjg5L3Jlc291cmNlcyIsIk15QXBpIl0sImNsaWVudF9pZCI6ImFwaWNsaWVudGlkIiwic3ViIjoic29tZS11bmlxdWUtaWQtMTIzNDU2Nzg5ODAiLCJhdXRoX3RpbWUiOjE1MDUwODIyODQsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsibXlhcGkiXSwiYW1yIjpbInB3ZCJdfQ.hQ60zzEbZOSVpP54yGAnnzfVEks18YXn3gU2wfFgNB33UxQabk1l3xkaeUPTpuFdmFTm4TbVatPaziGqaxjzYgfdVoAwQ3rYJMuYzOh0kUowKxXTkquAlD13ScpvxrGeCXGxFTRHrxX2h-1hHGQ9j2y2f3-ESynzrCdxp5HEH1271BSYfQ7pZIzvyxxpbmOzzKDzdYfcJV6ocnOU4jXBhw6iOzqpR03zxxtjIjGbJd2QwWklBGqZlO_thdZZFi-t7zu5eC4wqRCYGGZYWOUC17_Btc_Irg2SsvLCUDzsaBw7AVgLpZ7YjF-RsVqIi6oxNQ2K0zllzUy8VbupbWKr5Q' \
-H 'cache-control: no-cache' \
And now you should get past the [Authorize] atribute. Yay!
You now have one web application, which acts as both an Auth Server and a Resource Server.
Fun fact: with the above example the AddJwtBearer options specify the application's own url as an Authority, making the app request from itself the public key to use for validating the tokens. You could instead also use code to directly provide this key to the authentication middleware.

Perform a log-out using stormpath $http

I am trying to revoke oauth2 tokens using the stormpath API. Server-side authentication is performed using stormpath + express. Here is my request.
function revokeOauthTokens(params) {
// Revoke the oauth2 access. and refresh tokens
var oauthLogoutReq = {
method: 'POST',
url: params.apiBaseUrl + '/logout',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: 'grant_type=refresh_token&refresh_token='
+ params.oauth_tokens.refresh_token
}
return $http(oauthLogoutReq);
}
Where apiBaseUrl is my nodejs base url and ouath_tokens contains the response granted by a request to /oauth/token endpoint.
Looking at the documentation at the following links leaves me confused.
http://docs.stormpath.com/nodejs/express/latest/logout.html
http://docs.stormpath.com/guides/token-management/
Thanks.
This is a great question. As you’ve seen, express-stormpath is using secure, http-only cookies for token storage, and this doesn’t work for Cordova, Electron, etc, where cookie storage isn't implemented to spec. The alternative is local storage, or some other storage API that is provided to you (hopefully a secure one!).
The express-stormpath library does provide a /logout route, and it does revoke tokens, but it’s looking for the tokens in cookies. We need to add a new route, likely /oauth/revoke, to support explicit token revocation.
This is pretty easy to add right now as a custom route handler, and I’m including a link below. But please be aware that express-stormpath uses local token validation by default. This is done for speed (no roundtrip to our API) but the caveat is that your local server will NOT know that the tokens have been revoked, and can technically still be used for authentication if a malicious third party has stolen them from your client. If this is a concern you want to to address, you should opt-in to stormpath validation, which will always require a check against our token database. This is documented here:
http://docs.stormpath.com/nodejs/express/latest/authentication.html#token-validation-strategy
All that said, here is the route handler that you could wire up as /oauth/revoke, and have your Electron client use it to revoke the tokens when the user logs out:
'use strict';
var revokeToken = require('express-stormpath/lib/helpers/revoke-token');
function defaultResponder(res, err) {
if (err) {
console.error(err); // or your system logger
return res.status(err.status || 400).json({
message: err.developerMessage || err.message
});
}
res.end();
}
/**
* Implements the expected behavior of the /oauth/revoke endpoint, and requires
* that token_type be defined. This assumes that you are using the express-stormpath
* module, so that your Stormpath client and configuration context is available.
*
* #param {Object<ExpressRequest>} req Express JS Request
* #param {Object<ExpressResponse>} res Express JS Response
*/
function revokeTokens(req, res){
var client = req.app.get('stormpathClient');
var config = req.app.get('stormpathConfig');
var secret = config.client.apiKey.secret;
var token = req.body.token;
var token_type = req.body.token_type;
if (!token || ! token_type) {
defaultResponder(res, {
message: 'token and token_type fields are required'
});
}
if (token_type === 'access_token') {
revokeToken.revokeAccessToken(client, token, secret, defaultResponder.bind(null, res));
} else if (token_type === 'refresh_token') {
revokeToken.revokeRefreshToken(client, token, secret, defaultResponder.bind(null, res));
} else {
defaultResponder(res, {
message: 'invalid token_type'
});
}
}
module.exports = revokeTokens;
If you find that you don't want to use express-stormpath and would like to use something more direct, you can drop down to the Stormpath Node SDK and use it for token revocation:
https://docs.stormpath.com/nodejs/jsdoc/AccessToken.html
Or you can make DELETE requests directly against our API:
https://docs.stormpath.com/rest/product-guide/latest/auth_n.html#revoking-access-and-refresh-tokens
In both cases, you would be doing that from your server, not the Electron application.
I hope this helps!
-Robert

Access to Calendar API return 401 Unauthorized

I'm new to office 365 and having problem with accessing rest api.
I'm trying to test the rest api of Calendar and Mail API, so I decided to use Postman. However, to test those APIs, I need an access token in Authorization header. To figure out how to get a token, I decided to get the sample project here , configure, run and sign in on this local site to get the token cached in local storage and use that token for further requests in Postman. However, all requests I tested returned '401 unauthorized request'.
What I did:
Register a new app on Azure ADD associated with O365 account
Add full app permissions and delegated permissions.
Update 'oauth2AllowImplicitFlow' to true in manifest file.
Clone sample project
In app.js, I change the alter the content of config function as following
function config($routeProvider, $httpProvider, adalAuthenticationServiceProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/home.html',
controller: 'HomeController',
controllerAs: 'home',
requireADLogin: true
})
.otherwise({
redirectTo: '/'
});
// The endpoints here are resources for ADAL to get tokens for.
var endpoints = {
'https://outlook.office365.com': 'https://outlook.office365.com'
};
// Initialize the ADAL provider with your tenant name and clientID (found in the Azure Management Portal).
adalAuthenticationServiceProvider.init(
{
tenant: 'mytenantname.onmicrosoft.com',
clientId: '<my cliend Id>',
endpoints: endpoints,
cacheLocation: 'localStorage'
},
$httpProvider
);
};
Then I ran the app, it sign me in just fine and I can also get the token, but that token is also unauthorized to request.
I decoded the token and saw the value of 'aud', it didn't return "https://outlook.office365.com/". In this url, the author said that "This should be "https://outlook.office365.com/" for the Mail, Calendar, or Contacts APIs"
So what did I miss ?
How you call the Office 365 API in AngularJS?
When signing the user in, you will only get the id_token to authenticate the user.
The aud of id_token is the tenant id (GUID).
To call the Office 365 API, you need to use AugularJS http request.
Here is a sample of sending email using Microsoft Graph API in AngularJS:
// Build the HTTP request to send an email.
var request = {
method: 'POST',
url: 'https://graph.microsoft.com/v1.0/me/microsoft.graph.sendmail',
data: email
};
// Execute the HTTP request.
$http(request)
.then(function (response) {
$log.debug('HTTP request to Microsoft Graph API returned successfully.', response);
response.status === 202 ? vm.requestSuccess = true : vm.requestSuccess = false;
vm.requestFinished = true;
}, function (error) {
$log.error('HTTP request to Microsoft Graph API failed.');
vm.requestSuccess= false;
vm.requestFinished = true;
});
Before calling the API, ADAL.js will acquire another token - access token which you can used to send the email.
UPDATE#1
I also downloaded the sample you mentioned. To run this sample, please ensure you have the Exchange Online > Read and writer user mail Permission assigned in your application.