* feat: Add github authentication provider * feat: Change callback to always return a token, expect callbackUrl to point to FE * fix: Return login redirect URLas a 200 response
356 lines
10 KiB
TypeScript
356 lines
10 KiB
TypeScript
import {
|
|
AuthenticationInput,
|
|
AuthenticationResponse,
|
|
AuthIdentityProviderService,
|
|
AuthTypes,
|
|
Context,
|
|
DAL,
|
|
InternalModuleDeclaration,
|
|
ModuleJoinerConfig,
|
|
ModulesSdkTypes,
|
|
} from "@medusajs/types"
|
|
|
|
import { AuthIdentity, ProviderIdentity } from "@models"
|
|
|
|
import { joinerConfig } from "../joiner-config"
|
|
|
|
import {
|
|
InjectManager,
|
|
MedusaContext,
|
|
MedusaError,
|
|
MedusaService,
|
|
} from "@medusajs/utils"
|
|
import AuthProviderService from "./auth-provider"
|
|
|
|
type InjectedDependencies = {
|
|
baseRepository: DAL.RepositoryService
|
|
authIdentityService: ModulesSdkTypes.IMedusaInternalService<any>
|
|
providerIdentityService: ModulesSdkTypes.IMedusaInternalService<any>
|
|
authProviderService: AuthProviderService
|
|
}
|
|
export default class AuthModuleService
|
|
extends MedusaService<{
|
|
AuthIdentity: { dto: AuthTypes.AuthIdentityDTO }
|
|
ProviderIdentity: { dto: AuthTypes.ProviderIdentityDTO }
|
|
}>({ AuthIdentity, ProviderIdentity })
|
|
implements AuthTypes.IAuthModuleService
|
|
{
|
|
protected baseRepository_: DAL.RepositoryService
|
|
protected authIdentityService_: ModulesSdkTypes.IMedusaInternalService<AuthIdentity>
|
|
protected providerIdentityService_: ModulesSdkTypes.IMedusaInternalService<ProviderIdentity>
|
|
protected readonly authProviderService_: AuthProviderService
|
|
|
|
constructor(
|
|
{
|
|
authIdentityService,
|
|
providerIdentityService,
|
|
authProviderService,
|
|
baseRepository,
|
|
}: InjectedDependencies,
|
|
protected readonly moduleDeclaration: InternalModuleDeclaration
|
|
) {
|
|
// @ts-ignore
|
|
super(...arguments)
|
|
|
|
this.baseRepository_ = baseRepository
|
|
this.authIdentityService_ = authIdentityService
|
|
this.authProviderService_ = authProviderService
|
|
this.providerIdentityService_ = providerIdentityService
|
|
}
|
|
|
|
__joinerConfig(): ModuleJoinerConfig {
|
|
return joinerConfig
|
|
}
|
|
|
|
// @ts-expect-error
|
|
createAuthIdentities(
|
|
data: AuthTypes.CreateAuthIdentityDTO[],
|
|
sharedContext?: Context
|
|
): Promise<AuthTypes.AuthIdentityDTO[]>
|
|
|
|
createAuthIdentities(
|
|
data: AuthTypes.CreateAuthIdentityDTO,
|
|
sharedContext?: Context
|
|
): Promise<AuthTypes.AuthIdentityDTO>
|
|
|
|
@InjectManager("baseRepository_")
|
|
async createAuthIdentities(
|
|
data: AuthTypes.CreateAuthIdentityDTO[] | AuthTypes.CreateAuthIdentityDTO,
|
|
@MedusaContext() sharedContext: Context = {}
|
|
): Promise<AuthTypes.AuthIdentityDTO | AuthTypes.AuthIdentityDTO[]> {
|
|
const authIdentities = await this.authIdentityService_.create(
|
|
data,
|
|
sharedContext
|
|
)
|
|
|
|
return await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO[]>(
|
|
authIdentities,
|
|
{
|
|
populate: true,
|
|
}
|
|
)
|
|
}
|
|
|
|
// TODO: Update to follow convention
|
|
updateAuthIdentites(
|
|
data: AuthTypes.UpdateAuthIdentityDTO[],
|
|
sharedContext?: Context
|
|
): Promise<AuthTypes.AuthIdentityDTO[]>
|
|
|
|
updateAuthIdentites(
|
|
data: AuthTypes.UpdateAuthIdentityDTO,
|
|
sharedContext?: Context
|
|
): Promise<AuthTypes.AuthIdentityDTO>
|
|
|
|
@InjectManager("baseRepository_")
|
|
async updateAuthIdentites(
|
|
data: AuthTypes.UpdateAuthIdentityDTO | AuthTypes.UpdateAuthIdentityDTO[],
|
|
@MedusaContext() sharedContext: Context = {}
|
|
): Promise<AuthTypes.AuthIdentityDTO | AuthTypes.AuthIdentityDTO[]> {
|
|
const updatedUsers = await this.authIdentityService_.update(
|
|
data,
|
|
sharedContext
|
|
)
|
|
|
|
const serializedUsers = await this.baseRepository_.serialize<
|
|
AuthTypes.AuthIdentityDTO[]
|
|
>(updatedUsers, {
|
|
populate: true,
|
|
})
|
|
|
|
return Array.isArray(data) ? serializedUsers : serializedUsers[0]
|
|
}
|
|
|
|
async register(
|
|
provider: string,
|
|
authenticationData: AuthenticationInput
|
|
): Promise<AuthenticationResponse> {
|
|
try {
|
|
return await this.authProviderService_.register(
|
|
provider,
|
|
authenticationData,
|
|
this.getAuthIdentityProviderService(provider)
|
|
)
|
|
} catch (error) {
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
// @ts-expect-error
|
|
createProviderIdentities(
|
|
data: AuthTypes.CreateProviderIdentityDTO[],
|
|
sharedContext?: Context
|
|
): Promise<AuthTypes.ProviderIdentityDTO[]>
|
|
|
|
createProviderIdentities(
|
|
data: AuthTypes.CreateProviderIdentityDTO,
|
|
sharedContext?: Context
|
|
): Promise<AuthTypes.ProviderIdentityDTO>
|
|
|
|
@InjectManager("baseRepository_")
|
|
async createProviderIdentities(
|
|
data:
|
|
| AuthTypes.CreateProviderIdentityDTO[]
|
|
| AuthTypes.CreateProviderIdentityDTO,
|
|
@MedusaContext() sharedContext: Context = {}
|
|
): Promise<AuthTypes.ProviderIdentityDTO | AuthTypes.ProviderIdentityDTO[]> {
|
|
const providerIdentities = await this.providerIdentityService_.create(
|
|
data,
|
|
sharedContext
|
|
)
|
|
|
|
return await this.baseRepository_.serialize<
|
|
AuthTypes.ProviderIdentityDTO | AuthTypes.ProviderIdentityDTO[]
|
|
>(providerIdentities)
|
|
}
|
|
|
|
updateProviderIdentites(
|
|
data: AuthTypes.UpdateProviderIdentityDTO[],
|
|
sharedContext?: Context
|
|
): Promise<AuthTypes.ProviderIdentityDTO[]>
|
|
|
|
updateProviderIdentites(
|
|
data: AuthTypes.UpdateProviderIdentityDTO,
|
|
sharedContext?: Context
|
|
): Promise<AuthTypes.ProviderIdentityDTO>
|
|
|
|
@InjectManager("baseRepository_")
|
|
async updateProviderIdentites(
|
|
data:
|
|
| AuthTypes.UpdateProviderIdentityDTO
|
|
| AuthTypes.UpdateProviderIdentityDTO[],
|
|
@MedusaContext() sharedContext: Context = {}
|
|
): Promise<AuthTypes.ProviderIdentityDTO | AuthTypes.ProviderIdentityDTO[]> {
|
|
const updatedProviders = await this.providerIdentityService_.update(
|
|
data,
|
|
sharedContext
|
|
)
|
|
|
|
const serializedProviders = await this.baseRepository_.serialize<
|
|
AuthTypes.ProviderIdentityDTO[]
|
|
>(updatedProviders)
|
|
|
|
return Array.isArray(data) ? serializedProviders : serializedProviders[0]
|
|
}
|
|
|
|
async authenticate(
|
|
provider: string,
|
|
authenticationData: AuthenticationInput
|
|
): Promise<AuthenticationResponse> {
|
|
try {
|
|
return await this.authProviderService_.authenticate(
|
|
provider,
|
|
authenticationData,
|
|
this.getAuthIdentityProviderService(provider)
|
|
)
|
|
} catch (error) {
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
async validateCallback(
|
|
provider: string,
|
|
authenticationData: AuthenticationInput
|
|
): Promise<AuthenticationResponse> {
|
|
try {
|
|
return await this.authProviderService_.validateCallback(
|
|
provider,
|
|
authenticationData,
|
|
this.getAuthIdentityProviderService(provider)
|
|
)
|
|
} catch (error) {
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
getAuthIdentityProviderService(
|
|
provider: string
|
|
): AuthIdentityProviderService {
|
|
return {
|
|
retrieve: async ({ entity_id }) => {
|
|
const authIdentities = await this.authIdentityService_.list(
|
|
{
|
|
provider_identities: {
|
|
entity_id,
|
|
provider,
|
|
},
|
|
},
|
|
{
|
|
relations: ["provider_identities"],
|
|
}
|
|
)
|
|
|
|
if (!authIdentities.length) {
|
|
throw new MedusaError(
|
|
MedusaError.Types.NOT_FOUND,
|
|
`AuthIdentity with entity_id "${entity_id}" not found`
|
|
)
|
|
}
|
|
|
|
if (authIdentities.length > 1) {
|
|
throw new MedusaError(
|
|
MedusaError.Types.INVALID_DATA,
|
|
`Multiple authIdentities found for entity_id "${entity_id}"`
|
|
)
|
|
}
|
|
|
|
return await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO>(
|
|
authIdentities[0]
|
|
)
|
|
},
|
|
|
|
create: async (data: {
|
|
entity_id: string
|
|
provider_metadata?: Record<string, unknown>
|
|
user_metadata?: Record<string, unknown>
|
|
}) => {
|
|
const normalizedRequest = {
|
|
provider_identities: [
|
|
{
|
|
entity_id: data.entity_id,
|
|
provider_metadata: data.provider_metadata,
|
|
user_metadata: data.user_metadata,
|
|
provider,
|
|
},
|
|
],
|
|
}
|
|
|
|
const createdAuthIdentity = await this.authIdentityService_.create(
|
|
normalizedRequest
|
|
)
|
|
|
|
return await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO>(
|
|
createdAuthIdentity
|
|
)
|
|
},
|
|
update: async (
|
|
entity_id: string,
|
|
data: {
|
|
provider_metadata?: Record<string, unknown>
|
|
user_metadata?: Record<string, unknown>
|
|
}
|
|
) => {
|
|
const authIdentities = await this.authIdentityService_.list(
|
|
{
|
|
provider_identities: {
|
|
entity_id,
|
|
provider,
|
|
},
|
|
},
|
|
{
|
|
relations: ["provider_identities"],
|
|
}
|
|
)
|
|
|
|
if (!authIdentities.length) {
|
|
throw new MedusaError(
|
|
MedusaError.Types.NOT_FOUND,
|
|
`AuthIdentity with entity_id "${entity_id}" not found`
|
|
)
|
|
}
|
|
|
|
if (authIdentities.length > 1) {
|
|
throw new MedusaError(
|
|
MedusaError.Types.INVALID_DATA,
|
|
`Multiple authIdentities found for entity_id "${entity_id}"`
|
|
)
|
|
}
|
|
|
|
const providerIdentityData = authIdentities[0].provider_identities.find(
|
|
(pi) => pi.provider === provider
|
|
)
|
|
|
|
if (!providerIdentityData) {
|
|
throw new MedusaError(
|
|
MedusaError.Types.NOT_FOUND,
|
|
`ProviderIdentity with entity_id "${entity_id}" not found`
|
|
)
|
|
}
|
|
|
|
const updatedProviderIdentity =
|
|
await this.providerIdentityService_.update({
|
|
id: providerIdentityData.id,
|
|
...data,
|
|
})
|
|
|
|
const serializedResponse =
|
|
await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO>(
|
|
authIdentities[0]
|
|
)
|
|
const serializedProviderIdentity =
|
|
await this.baseRepository_.serialize<AuthTypes.ProviderIdentityDTO>(
|
|
updatedProviderIdentity
|
|
)
|
|
|
|
serializedResponse.provider_identities = [
|
|
...(serializedResponse.provider_identities?.filter(
|
|
(p) => p.provider !== provider
|
|
) ?? []),
|
|
serializedProviderIdentity,
|
|
]
|
|
|
|
return serializedResponse
|
|
},
|
|
}
|
|
}
|
|
}
|