Authentication
Login Objects
Session:
dataset:authorizationuid; The UID of the user (assigned by the authorization process -- uid associated with the specific user)
dataset:loginaccountinfo; This is the dataset:LoginAccountInformation catalog object
dataset:UserAccountRole: This is the dataset:UserAccount catalog object.
dataset:loginstage: This is the status of the login process.
prov:SoftwareAgent:
vcard:Individual: This is the dataset:DatabasePerson catalog object.
user
token: This is the session token. It is used to authenticate calls to the server.
Catalog objects:
dataset:LoginAccountInformation: This is created upon authorization.
dataset:NewUserAccount: Using the additional user information (from the interface ) the dataset:UserAccount and dataset:DatabasePerson are created using the transaction dataset:InitializeUserAccount.
dataset:UserAccount: This has the information about the user account, including the authorization information and most notably the dataset:UserAccountRole, which governs what the account can do with respect to the database.
dataset:DatabasePerson : This is information relative the to person who owns the user account.
Status:
dataset:LoginAuthenticated: At the first login stage, the dataset:LoginAccountInformation is written to the database during the server for the first login. This has the system authentification information. The server identifies this stage by trying to read the dataset:LoginAccountInformation. If it is not there, this is the first call after authentification.
dataset:LoginAccountInformation: dataset:LoginAccountInformation. exists, but not dataset:UserAccount and dataset:DatabasePerson.
dataset:LoginRegistration: Both the dataset:UserAccount and dataset:DatabasePerson exist.
Login Flow:
In the login window /sign-in (SignInComponent) the method of authentication is chosen. For the providers, Google, Facebook and Github, the corresponding 'provider' is given and through the AuthService.AuthLogin method is called
AuthService.AuthLogin:
This clears the session variables and then using the AngularFireAuth service, the signup window is setup and the result observable, meaning the authentification user information is given. With this user, the AuthService.SetUserData is called:
With getAuth().currentUser.getIdToken(true).then((token) the token is retrieved (as an observable).
If either the user object (session.getAuthorizationData()) is null, which means this is the very first login call, or the user account is null (session.getUserAccount()), which means the UserAccount has not been set up yet, then a login transaction has to be made with getUserInformationFromServer. Otherwise, everything is already set up and nothing further, in terms of login occurs.
Here, the The userdata is stored with session.setAuthorizationData and the session token is stored: session.setToken.
With the token and the user data, the first login transaction input is setup. This is sent to getUserInformationFromServer to call the server login.
AuthService.getUserInformationFromServer
This method is called when not all the user account information has been set up. It will use the status to determine what will be done.
This method calls the service /login (LoginService) with essentually the authentification user information and the token. If the call fails, then the session will be cleared and nothing happens.
If the session succeeds the status is retrieved and set in the session.
If the status is dataset:LoginAuthenticated, this means that this is the very first call and the dataset:LoginAccountInformation catalog object is created.
Implementation
Angular UI
https://www.positronx.io/full-angular-7-firebase-authentication-system/
or maybe this one:
https://github.com/AnthonyNahas/ngx-auth-firebaseui
Notes on Authentication:
https://github.com/angular/angularfire/blob/master/docs/auth/getting-started.md
This is the general pattern.
The result of the authenticationn is
The User information:
SetUserData(user) {
const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.uid}`);
const userData: User = {
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
emailVerified: user.emailVerified
}
return userRef.set(userData, {
merge: true
})
}
A token is generated with the
Manage Users:
https://firebase.google.com/docs/auth/admin/manage-users#java
Retrieve User data for the backend:
UserRecord userRecord = FirebaseAuth.getInstance().getUser(uid);
// See the UserRecord reference doc for the contents of userRecord.
System.out.println("Successfully fetched user data: " + userRecord.getUid());
or
UserRecord userRecord = FirebaseAuth.getInstance().getUserByEmail(email);
// See the UserRecord reference doc for the contents of userRecord.
System.out.println("Successfully fetched user data: " + userRecord.getEmail());
Create a user:
CreateRequest request = new CreateRequest()
.setEmail("user@example.com")
.setEmailVerified(false)
.setPassword("secretPassword")
.setPhoneNumber("+11234567890")
.setDisplayName("John Doe")
.setPhotoUrl("http://www.example.com/12345678/photo.png")
.setDisabled(false);
UserRecord userRecord = FirebaseAuth.getInstance().createUser(request);
System.out.println("Successfully created new user: " + userRecord.getUid());
API_KEY:
The API_KEY for blurock-database is AIzaSyDru7mwaZdl1kh8EVAr2QwkWK2gTaJnFUk
TokenIDs
https://firebase.google.com/docs/database/rest/auth#firebase_id_tokens
Verify ID tokens using the Firebase Admin SDK
// idToken comes from the client app (shown above)
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
String uid = decodedToken.getUid();
https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients
When a user or device successfully signs in, Firebase creates a corresponding ID token that uniquely identifies them and grants them access to several resources, such as Firebase Realtime Database and Cloud Storage. You can re-use that ID token to identify the user or device on your custom backend server. To retrieve the ID token from the client, make sure the user is signed in and then get the ID token from the signed-in user:
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
Get TokenID in Angular:
The fix for your code is to return a Promise, instead of trying to return the value:
GetToken(): Promise<string> {
return new Promise((resolve, reject) => {
this.afAuth.auth.onAuthStateChanged( user => {
if (user) {
user.getIdToken().then(idToken => {
this.userToken = idToken;
resolve(idToken);
});
}
});
})
}
In words: GetToken returns a promise that resolves once an ID token is available. If you know the user is already signed in when you call this function, you can simplify it to:
GetToken(): string {
const user = firebase.authentication().currentUser;
return user.getIdToken()
}
The difference is that the second function does not wait for the user to be signed in, so will fail if there is no signed in user.
Asynchronous
For more on this see How to return value from an asynchronous callback function? I highly recommend studying this answer for a while, as this asynchronous behavior is incredibly common when dealing with web APIs.
https://www.nerd.vision/post/using-firebase-auth-in-angular-components-synchronously
Send TokenID from client to Server
Sending a request from Angular:
postRequest() {
const url = 'https://your-endpoint';
firebase.auth().currentUser.getIdToken()
.then(authToken => {
const headers = new Headers({'Authorization': 'Bearer ' + authToken });
return this.http.post(url, { someData } , { headers }).toPromise()
})
}
This is the code on the backend, in Node.js.... to be translated to JAVA
const admin = require('firebase-admin');
admin.initializeApp(yourConfig);
const express = require('express')
const app = express()
app.post('/your-endpoint', (req, res) => {
const token = req.headers.authorization.split('Bearer ')[1]
return admin.auth().verifyIdToken(token)
.then(decodedToken => {
const uid = decodedToken.uid;
res.status(200).send('Looks good!')
})
.catch(err => res.status(403).send('Unauthorized'))
});
Get the Authorization token from the request (the 'Bearer')
Verify the token
Decode token to get the userid.
http://www.avajava.com/tutorials/lessons/how-do-i-use-basic-authentication-with-tomcat.html
package test;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.buf.Base64;
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("This is the Test Servlet");
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
out.print("<br/>Header Name: <em>" + headerName);
String headerValue = request.getHeader(headerName);
out.print("</em>, Header Value: <em>" + headerValue);
out.println("</em>");
}
out.println("<hr/>");
String authHeader = request.getHeader("authorization");
String encodedValue = authHeader.split(" ")[1];
out.println("Base64-encoded Authorization Value: <em>" + encodedValue);
String decodedValue = Base64.base64Decode(encodedValue);
out.println("</em><br/>Base64-decoded Authorization Value: <em>" + decodedValue);
out.println("</em>");
}
}
Critical code:
String authHeader = request.getHeader("authorization");
String encodedValue = authHeader.split(" ")[1];
out.println("Base64-encoded Authorization Value: <em>" + encodedValue);
String decodedValue = Base64.base64Decode(encodedValue);
out.println("</em><br/>Base64-decoded Authorization Value: <em>" + decodedValue);
out.println("</em>");
Backend
Maven
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>8.1.0</version>
</dependency>
These are the docs to Add Firebase to Backend.
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.getApplicationDefault())
.setProjectId("<FIREBASE_PROJECT_ID>")
.build();
FirebaseApp.initializeApp(options);
When calling backend
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'JWT ' + 'ey.....'
}),
};