Skip to content

Commit

Permalink
Merge pull request #1883 from akto-api-security/feature/oidc_sso
Browse files Browse the repository at this point in the history
feat: okta oidc sso
  • Loading branch information
Ark2307 authored Jan 1, 2025
2 parents b79197b + 9ebaf00 commit ea1f09c
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public String execute() {
if (GithubLogin.getGithubUrl() != null) {
servletRequest.setAttribute("githubUrl", GithubLogin.getGithubUrl());
}
if(OktaLogin.getAuthorisationUrl() != null){
if(DashboardMode.isOnPremDeployment() && OktaLogin.getAuthorisationUrl() != null){
servletRequest.setAttribute("oktaAuthUrl", new String(Base64.getEncoder().encode(OktaLogin.getAuthorisationUrl().getBytes())));
}
if (InitializerListener.aktoVersion != null && InitializerListener.aktoVersion.contains("akto-release-version")) {
Expand Down
76 changes: 48 additions & 28 deletions apps/dashboard/src/main/java/com/akto/action/SignupAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -509,32 +509,38 @@ public String registerViaGithub() {
}

public String registerViaOkta() throws IOException{
if (!DashboardMode.isOnPremDeployment()) return Action.ERROR.toUpperCase();
OktaLogin oktaLoginInstance = OktaLogin.getInstance();
if(oktaLoginInstance == null){
servletResponse.sendRedirect("/login");
return ERROR.toUpperCase();
}
try {
Config.OktaConfig oktaConfig;
if(DashboardMode.isOnPremDeployment()) {
OktaLogin oktaLoginInstance = OktaLogin.getInstance();
if(oktaLoginInstance == null){
servletResponse.sendRedirect("/login");
return ERROR.toUpperCase();
}

Config.OktaConfig oktaConfig = OktaLogin.getInstance().getOktaConfig();
if (oktaConfig == null) {
servletResponse.sendRedirect("/login");
return ERROR.toUpperCase();
}
setAccountId(1000000);
oktaConfig = OktaLogin.getInstance().getOktaConfig();
} else {
setAccountId(Integer.parseInt(state));
oktaConfig = Config.getOktaConfig(accountId);
}
if(oktaConfig == null) {
servletResponse.sendRedirect("/login");
return ERROR.toUpperCase();
}

String domainUrl = "https://" + oktaConfig.getOktaDomainUrl() + "/oauth2/" + oktaConfig.getAuthorisationServerId() + "/v1";
String clientId = oktaConfig.getClientId();
String clientSecret = oktaConfig.getClientSecret();
String redirectUri = oktaConfig.getRedirectUri();
String domainUrl = "https://" + oktaConfig.getOktaDomainUrl() + "/oauth2/" + oktaConfig.getAuthorisationServerId() + "/v1";
String clientId = oktaConfig.getClientId();
String clientSecret = oktaConfig.getClientSecret();
String redirectUri = oktaConfig.getRedirectUri();

BasicDBObject params = new BasicDBObject();
params.put("grant_type", "authorization_code");
params.put("code", this.code);
params.put("client_id", clientId);
params.put("client_secret", clientSecret);
params.put("redirect_uri", redirectUri);
BasicDBObject params = new BasicDBObject();
params.put("grant_type", "authorization_code");
params.put("code", this.code);
params.put("client_id", clientId);
params.put("client_secret", clientSecret);
params.put("redirect_uri", redirectUri);

try {
Map<String,Object> tokenData = CustomHttpRequest.postRequestEncodedType(domainUrl +"/token",params);
String accessToken = tokenData.get("access_token").toString();
Map<String,Object> userInfo = CustomHttpRequest.getRequest( domainUrl + "/userinfo","Bearer " + accessToken);
Expand All @@ -544,7 +550,7 @@ public String registerViaOkta() throws IOException{
SignupInfo.OktaSignupInfo oktaSignupInfo= new SignupInfo.OktaSignupInfo(accessToken, username);

shouldLogin = "true";
createUserAndRedirect(email, username, oktaSignupInfo, 1000000, Config.ConfigType.OKTA.toString());
createUserAndRedirect(email, username, oktaSignupInfo, accountId, Config.ConfigType.OKTA.toString(), RBAC.Role.MEMBER);
code = "";
} catch (Exception e) {
loggerMaker.errorAndAddToDb("Error while signing in via okta sso \n" + e.getMessage(), LogDb.DASHBOARD);
Expand All @@ -560,7 +566,7 @@ public String registerViaOkta() throws IOException{
public String sendRequestToSamlIdP() throws IOException{
String queryString = servletRequest.getQueryString();
String emailId = Util.getValueFromQueryString(queryString, "email");
if(emailId.length() == 0){
if(emailId.isEmpty()){
code = "Error, user email cannot be empty";
logger.error(code);
servletResponse.sendRedirect("/login");
Expand All @@ -569,11 +575,10 @@ public String sendRequestToSamlIdP() throws IOException{
logger.info("Trying to sign in for: " + emailId);
setUserEmail(emailId);
SAMLConfig samlConfig = SSOConfigsDao.instance.getSSOConfig(userEmail);
if(samlConfig == null){
code = "Error, cannot login via SSO, redirecting to login";
if(samlConfig == null) {
code = "Error, cannot login via SSO, trying to login with okta sso";
logger.error(code);
servletResponse.sendRedirect("/login");
return ERROR.toUpperCase();
return oktaAuthUrlCreator(emailId);
}
int tempAccountId = Integer.parseInt(samlConfig.getId());
logger.info("Account id: " + tempAccountId + " found for " + emailId);
Expand All @@ -599,6 +604,21 @@ public String sendRequestToSamlIdP() throws IOException{
return SUCCESS.toUpperCase();
}

public String oktaAuthUrlCreator(String emailId) throws IOException {
logger.info("Trying to create auth url for okta sso for: " + emailId);
Config.OktaConfig oktaConfig = Config.getOktaConfig(emailId);
if(oktaConfig == null) {
code= "Error, cannot find okta sso for this organization, redirecting to login";
logger.error(code);
servletResponse.sendRedirect("/login");
return ERROR.toUpperCase();
}

String authorisationUrl = OktaLogin.getAuthorisationUrl(emailId);
servletResponse.sendRedirect(authorisationUrl);
return SUCCESS.toUpperCase();
}

public String registerViaAzure() throws Exception{
Auth auth;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ public class OktaSsoAction extends UserAction {
private String redirectUri;

public String addOktaSso() {

if(!DashboardMode.isOnPremDeployment()){
addActionError("This feature is only available in on-prem deployment");
return ERROR.toUpperCase();
}

if (SsoUtils.isAnySsoActive()) {
addActionError("A SSO Integration already exists.");
return ERROR.toUpperCase();
Expand All @@ -41,20 +35,30 @@ public String addOktaSso() {
oktaConfig.setAuthorisationServerId(authorisationServerId);
oktaConfig.setOktaDomainUrl(oktaDomain);
oktaConfig.setRedirectUri(redirectUri);

if(!DashboardMode.isOnPremDeployment()){
oktaConfig.setAccountId(Context.accountId.get());
String userLogin = getSUser().getLogin();
String domain = userLogin.split("@")[1];
oktaConfig.setOrganizationDomain(domain);
}
ConfigsDao.instance.insertOne(oktaConfig);

return SUCCESS.toUpperCase();
}

public String deleteOktaSso() {
if(!DashboardMode.isOnPremDeployment()){
addActionError("This feature is only available in on-prem deployment");
return ERROR.toUpperCase();
DeleteResult result;
if(DashboardMode.isOnPremDeployment()) {
result = ConfigsDao.instance.deleteAll(Filters.eq("_id", "OKTA-ankush"));
} else {
result = ConfigsDao.instance.deleteAll(
Filters.and(
Filters.eq("_id", "OKTA-ankush"),
Filters.eq(Config.OktaConfig.ACCOUNT_ID, Context.accountId.get())
)
);
}

DeleteResult result = ConfigsDao.instance.deleteAll(Filters.eq("_id", "OKTA-ankush"));

if (result.getDeletedCount() > 0) {
for (Object obj : UsersDao.instance.getAllUsersInfoForTheAccount(Context.accountId.get())) {
BasicDBObject detailsObj = (BasicDBObject) obj;
Expand All @@ -68,13 +72,13 @@ public String deleteOktaSso() {

@Override
public String execute() throws Exception {

if(!DashboardMode.isOnPremDeployment()){
addActionError("This feature is only available in on-prem deployment");
return ERROR.toUpperCase();
Config.OktaConfig oktaConfig;
if(DashboardMode.isOnPremDeployment()) {
oktaConfig = (Config.OktaConfig) ConfigsDao.instance.findOne("_id", "OKTA-ankush");
} else {
String email = getSUser().getLogin();
oktaConfig = Config.getOktaConfig(email);
}

Config.OktaConfig oktaConfig = (Config.OktaConfig) ConfigsDao.instance.findOne("_id", "OKTA-ankush");
if (SsoUtils.isAnySsoActive() && oktaConfig == null) {
addActionError("A different SSO Integration already exists.");
return ERROR.toUpperCase();
Expand Down
17 changes: 16 additions & 1 deletion apps/dashboard/src/main/java/com/akto/utils/OktaLogin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;


import com.akto.dao.ConfigsDao;
Expand Down Expand Up @@ -55,6 +54,22 @@ public static String getAuthorisationUrl() {
return authUrl;
}

public static String getAuthorisationUrl(String email) {
OktaConfig oktaConfig = Config.getOktaConfig(email);

Map<String, String> paramMap = new HashMap<>();
paramMap.put("client_id", oktaConfig.getClientId());
paramMap.put("redirect_uri",oktaConfig.getRedirectUri());
paramMap.put("response_type", "code");
paramMap.put("scope", "openid%20email%20profile");
paramMap.put("state", String.valueOf(oktaConfig.getAccountId()));

String queryString = SsoUtils.getQueryString(paramMap);

String authUrl = "https://" + oktaConfig.getOktaDomainUrl() + "/oauth2/" + oktaConfig.getAuthorisationServerId() + "/v1/authorize?" + queryString;
return authUrl;
}

private OktaLogin() {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react'
import CopyCommand from '../../../components/shared/CopyCommand';
import IntegrationsLayout from './IntegrationsLayout';
import { Button, Form, FormLayout, HorizontalStack, LegacyCard, Text, TextField } from '@shopify/polaris';
import { Button, Form, FormLayout, HorizontalStack, LegacyCard, Link, Text, TextField, VerticalStack } from '@shopify/polaris';
import func from "@/util/func"
import settingRequests from '../api';
import SpinnerCentered from "../../../components/progress/SpinnerCentered"
Expand All @@ -22,7 +22,7 @@ function OktaIntegration() {
const [oktaDomain, setOktaDomain] = useState('')
const [authorizationServerId, setAuthorizationServerId] = useState('')
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [nextButtonActive,setNextButtonActive] = useState(window.DASHBOARD_MODE === "ON_PREM")
const [nextButtonActive,setNextButtonActive] = useState(true)

const redirectUri = hostname + "/authorization-code/callback"

Expand Down Expand Up @@ -135,6 +135,19 @@ function OktaIntegration() {
},[])

const cardContent = "Enable login via Okta SSO in your dashboard."

const useCardContent = (
<VerticalStack gap={"2"}>
<Text>{cardContent}</Text>
<HorizontalStack gap={"1"}>
<Text>Use</Text>
<Link>https://app.akto.io/sso-login</Link>
<Text>for signing into AKTO dashboard via SSO.</Text>
</HorizontalStack>
</VerticalStack>
)


const oktaSSOComponent = (
loading ? <SpinnerCentered /> :
<LegacyCard title="Okta SSO">
Expand All @@ -145,7 +158,7 @@ function OktaIntegration() {

return (
<>
<IntegrationsLayout title="Okta SSO" cardContent={cardContent} component={oktaSSOComponent} docsUrl="https://docs.akto.io/sso/okta-oidc"/>
<IntegrationsLayout title="Okta SSO" cardContent={useCardContent} component={oktaSSOComponent} docsUrl="https://docs.akto.io/sso/okta-oidc"/>
<DeleteModal showDeleteModal={showDeleteModal} setShowDeleteModal={setShowDeleteModal} SsoType={"Okta"} onAction={handleDelete} />
</>
)
Expand Down
48 changes: 47 additions & 1 deletion libs/dao/src/main/java/com/akto/dto/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.HashSet;
import java.util.Set;

import com.akto.dao.ConfigsDao;
import com.mongodb.client.model.Filters;
import org.bson.codecs.pojo.annotations.BsonDiscriminator;

@BsonDiscriminator
Expand Down Expand Up @@ -356,7 +358,11 @@ public static class OktaConfig extends Config {
private String oktaDomainUrl;
private String authorisationServerId;
private String redirectUri;

public static final String ORGANIZATION_DOMAIN = "organizationDomain";
private String organizationDomain;
public static final String ACCOUNT_ID = "accountId";
private int accountId;

public static final String CONFIG_ID = ConfigType.OKTA.name() + CONFIG_SALT;

public OktaConfig() {
Expand Down Expand Up @@ -399,6 +405,20 @@ public String getRedirectUri() {
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}

public String getOrganizationDomain() {
return organizationDomain;
}
public void setOrganizationDomain(String organizationDomain) {
this.organizationDomain = organizationDomain;
}

public int getAccountId() {
return accountId;
}
public void setAccountId(int accountId) {
this.accountId = accountId;
}
}

@BsonDiscriminator
Expand Down Expand Up @@ -664,4 +684,30 @@ public static boolean isConfigSSOType(ConfigType configType){
}
return ssoConfigTypes.contains(configType);
}

public static OktaConfig getOktaConfig(int accountId) {
OktaConfig config = (OktaConfig) ConfigsDao.instance.findOne(
Filters.and(
Filters.eq("_id", "OKTA-ankush"),
Filters.eq(OktaConfig.ACCOUNT_ID, accountId)
)
);
return config;
}

public static OktaConfig getOktaConfig(String userEmail){
if (userEmail == null || userEmail.trim().isEmpty()) {
return null;
}
String[] companyKeyArr = userEmail.split("@");
if(companyKeyArr == null || companyKeyArr.length < 2){
return null;
}

String domain = companyKeyArr[1];
OktaConfig config = (OktaConfig) ConfigsDao.instance.findOne(
Filters.eq(OktaConfig.ORGANIZATION_DOMAIN, domain)
);
return config;
}
}

0 comments on commit ea1f09c

Please sign in to comment.