
What is Single Sign-On (SSO)?
Single Sign-On (SSO) is a user authentication process allowing individuals to access multiple applications with a single login credentials. Instead of remembering different usernames and passwords for each app or service, users sign in once and can access all connected systems without re-entering their credentials.
Here’s an easy way to think of it: imagine you have a single key that unlocks the doors to your home, car, and office. Once you use that key to unlock one door, you can access all the others without needing separate keys. SSO works similarly — once logged in through one system, you don’t need to log in again to access other connected apps or services.
SSO is often powered by protocols like OAuth 2.0 or OpenID Connect, which securely handle sharing login information between systems. Big companies like Google, Facebook, and Microsoft offer SSO, allowing users to log in to many websites using their Google or Facebook accounts. This makes logging in faster and more convenient while also reducing password fatigue.
In this article, we are going to implement Google Login using ReactJS for the client-side application and Golang for the server-side application.
Steps we are going to follow :
- Client-Side Implementation: Start by building the client-side application.
- Authentication: Implement authentication to obtain the access token.
- Access Token Retrieval: Retrieve the access token after the user logs in.
- Send Token to Server: Forward the access token to the server for validation.
- User Verification (OIDC Protocol): Verify the user’s identity using the OIDC protocol on the server.
- JWT Generation: Once verified, extract user information (e.g., email), generate a JWT token, and send it back to the client.
- Authorization: Use the JWT token for client-side authorization.
Client-Side Application :
In the client-side application, we will use one react-js library, React OAuth2 For Google. There are three options are there for Google login, you can explore by visiting the above link. Now here we can use the Custom Login Option.
To integrate Google OAuth2 using the react-oauth/google library, you must first create an application in the Google Developer Console and generate a Client ID. Here’s a step-by-step process:
1. Create an Application in Google Developer Console
- Navigate to the Google Developer Console.
- Create a new project, or select an existing one.
- Go to APIs & Services → Credentials and click Create Credentials.
- Choose OAuth 2.0 Client IDs, configure the consent screen, and add your application’s authorized redirect URIs.
- After creation, you will receive a Client ID that will be used in your React application.
2. Integrate Google OAuth in React Application:
Install the required library:
npm install @react-oauth/google
In your React app, wrap your main component with the GoogleOAuthProvider using the Client ID in the index.js file:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
// Import GoogleOAuthProvider from react-oauth/google to enable Google OAuth
import { GoogleOAuthProvider } from '@react-oauth/google';
// Define your Google Client ID obtained from Google Developer Console
const clientId = "YOUR_GOOGLE_CLIENT_ID";
// Create a root element for rendering the app
const root = ReactDOM.createRoot(document.getElementById('root'));
// Render the app wrapped in GoogleOAuthProvider for Google OAuth functionality
root.render(
<GoogleOAuthProvider clientId={clientId}>
<App />
</GoogleOAuthProvider>
);
3. Using Custom Google Login :
In your App.js, implement a custom Google login by using the useGoogleLogin hook from the library:
// Import React library for building user interfaces
import React from 'react';
// Import Google login hook from react-oauth/google library
import { useGoogleLogin } from '@react-oauth/google';
const App = () => {
// Initialize Google login with success and error handlers
const login = useGoogleLogin({
// Log the response upon successful login
onSuccess: (response) => console.log(response),
// Log any error that occurs during the login process
onError: (error) => console.error(error),
});
return (
<div>
// Button to trigger Google login when clicked
<button onClick={() => login()}>Login with Google</button>
</div>
);
};
export default App;
From above we can get the access token as the tokenResponse and we are going to send it to the server-side application using a particular API. You know for the SSO some protocols are there the OAuth2 library covering the part of the Oauth2 protocol.
Server-Side Application :
We are getting the access token in the request body and have to verify that access token.
google_api_url : https://www.googleapis.com/oauth2/v3/userinfo
Using the above link we can verify the user by passing the access token in their header. After verification, we get the user information and using that use information we are using it for generate the JWT token then again we send the JWT token in the login response
// Login function to handle user login via an HTTP request.
func Login(ctx context.Context, serviceName service.Service) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
// Set headers for the request, defining the content type as JSON.
headers := map[string]string{
"Content-Type": "application/json",
}
// Send a request to the Google API using the provided access token.
body, err := SendRequest(ctx, http.MethodGet, google_api_url, req.AccessToken, nil, headers)
if err != nil {
fmt.Println("error is : ", err) // Print error if request fails.
return
}
// Generate a JWT token based on the user information.
token, err := GenerateLoginToken(ctx, userInfo)
if err != nil{
fmt.Println("error is : ", err) // Print error if token generation fails.
return
}
// Set the Authorization header with the generated JWT token and send a success response.
w.Header().Set("Authorization", "Bearer "+token)
SuccessResponse(w, http.StatusOK, token)
}
}
}
}
// SendRequest sends an HTTP request with the given method, URL, access token, body, and headers.
func SendRequest(ctx context.Context, methodType, url, accessToken string, body io.Reader, headers map[string]string) ([]byte, error) {
// Create a new HTTP request with the provided context, method, URL, and body.
serverRequest, err := http.NewRequestWithContext(ctx, methodType, url, body)
if err != nil {
return nil, err // Return error if request creation fails.
}
// Set headers for the request.
for key, value := range headers {
serverRequest.Header.Set(key, value)
}
// Execute the HTTP request.
resp, err := http.DefaultClient.Do(serverRequest)
if err != nil {
return nil, err // Return error if request execution fails.
}
defer resp.Body.Close() // Ensure response body is closed after reading.
// Read and return the response body.
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err // Return error if reading response body fails.
}
return respBody, nil
}
As we discussed already, some protocols are there for the SSO, so fetching the user information is one of the processes of that protocol some part is covered in the client-side application, and the remaining part is covered in the server-side application.
Conclusion :
In this blog, we’ve explored the client-side as well as the server-side approach to handling OAuth2 authentication. However, there’s another method that involves managing the entire authentication process on the backend using Golang.
Stay tuned for my upcoming post, where I’ll dive deep into how to implement OAuth2 authentication entirely on the backend with Golang. It’s an efficient alternative that provides more flexibility and control. Coming soon!
